How to check whether response JSON of an API is empty or has an error? - reactjs

I am new to reactjs and I am stuck in one problem. I am calling an Update API which is of PUT type. I use the fetch function to call the API in reactjs and I check the response of the API. If Response is 200 OK, then I return the response.json() and then check whether the json object has error in it or not. If it has error, then I print the error else I update it.
But when there is no Error present in the response, then I get a syntax-error in return response.json() statement and If there is actually a Error present in the response then there is no syntax-error shown. So is there a method to check whether the response is empty or not so that accordingly I can return response.json().
I have tried by putting a condition as if(response.json() != '') but it shows error in response.json() statement.
fetch( API + name , {
method: 'PUT',
headers: {
'Accept' : 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('access_token')
},
body: JSON.stringify({
name: name,
description: updateDesc
}),
}).then(function(response) {
if(response.status == '200'){
flag=true;
return response.json();
}
else {
flag=false
}
})
.then(json => {
if(flag)
{
if(json.Error != "")
{
that.createNotification('error','update');
}
else {
this.createNotification('success','update');
}
}
});
Unhandled Rejection (SyntaxError): Unexpected end of JSON input

There are multiple issues with this imo:
The callback should be refactored to avoid the use of the flag variable. The code in the function supplied to handlers like then, catch and finally of promises is executed asynchronously. Therefore you cannot be sure / (should not assume) when this value will be assigned and in which state your context is at that time.
.then(json => { if there is an error this will actually use the promise returned by fetch aka response and not the promise returned by response.json() (Currently return response.json() is only executed in the success case)
Note that this happens (currently works in the error case) because you can chain promises. You can find more info and examples about this here
I would refactor the handling of the fetch promise like this:
You can shorten the following example and avoid assigning the promises, but it makes the example better readable
More information about the response object
const fetchPromise = fetch(<your params>);
fetchPromise.then(response => {
if (response.ok()){
//Your request was successful
const jsonPromise = response.json();
jsonPromise.then(data => {
console.log("Successful request, parsed json body", data);
}).catch(error => {
//error handling for json parsing errors (empty body etc.)
console.log("Successful request, Could not parse body as json", error);
})
} else {
//Your request was not successful
/*
You can check the body of the response here anyways. Maybe your api does return a json error?
*/
}
}).catch(error => {
//error handling for fetch errors
}))

Related

sveltekit TypeError: immutable

I'm using sveltekit 1.0.0-next.483 running with npm run dev -- --host
connecting to an endpoint with a mobile device i get this error:
typeError: immutable
at Headers.append ([..]node_modules/undici/lib/fetch/headers.js:227:13)
This error only occurs on mobile device, connecting to the local net ip address.
my endpoint: src/routes/gqlendpoint/+server.ts
const base = 'http://localhost:4000/graphql';
export async function POST( opts: { request: Request} ): Promise<Response> {
const { request } = opts;
const body = await request.json();
const response = await fetch(base, {
//credentials:"include",
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return response;
}
the only way I found to unlock this situation, is by commenting a line of code inside node_modules/undici/lib/fetch/headers.js
// 3. If headers’s guard is "immutable", then throw a TypeError.
// 4. Otherwise, if headers’s guard is "request" and name is a
// forbidden header name, return.
// Note: undici does not implement forbidden header names
if (this[kGuard] === 'immutable') {
**//throw new TypeError('immutable')**
} else if (this[kGuard] === 'request-no-cors') {
// 5. Otherwise, if headers’s guard is "request-no-cors":
// TODO
}
which is certainly not a good solution.
You have to return new Response body to avoid this issue , see example code below.
return new Response('test example')
in place of return response;

how intercept and stub the response of a rpc call in react with cypress

I want to intercept a rpc call that I made to the api in my react app. I'm using a custom hook that receives the buffer and the rpc method that I want to call and returns the data(something like react-query useQuery hook).
The thing is because of being a rpc call, the request urls of my requests are all the same and the response is binary, I can't distinguish the requests and intercept the one to stub.
One example of making a rpc call:
const {response, loading, error} = useRpc({
Buffer: GetUser,
Request: GetUserRequest
});
Edit 1:
I'm using
cy.fixture('fixutre-file').then((data) => {
const response = new TextDecoder().decode(res.body);
cy.intercept('https://example.com/', { method: 'POST' },
(req) => {
req.continue((res) => {
if ("some condition for distinguishing the request I want to intercept, here") {
res.send({ fixture: 'fixutre-file' });
}
});
});
}):
to get the response and decide whether or not intercept this req and instead send back my fixture data. But the response constant is still some unreadable string. What's wrong with my approach?
Edit 2:
Another approach that I used, was to use the cypress-protobuf package and encode my fixture.json file with the related protobuffer file:
cy.fixture('fixutre-file').then((data) => {
cy.task('protobufEncode', {
fixtureBody: data,
message: 'the_message',
protoFilePath: './protobuf/protofile.proto'
}).then((encodedData) => {
cy.intercept('https://example.com/', { method: 'POST' },
(req) => {
/////// approach 1(didn't work): ///////
// can't use this approach(because there is no identifier on
// req object to distinguish the requests I want to
// intercept)
// if ("some condition based on `req` here") {
// req.reply(encodedData);
// } else {
// req.continue();
// }
/////// approach 2: ///////
// using encodedData to compare it with res.body
req.continue(res => {
// can't compare res.body with encodedData, because
// encodedData is an empty string!
});
}).as('needToWait');
cy.wait('#needToWait').get('some selector').should('exist')
});
}):
Now the problem is:
encodedData is just an empty string, meaning it didn't work, so I can't compare the response with my fixture data to intercept the related request
You can simply check for some value from the request that distinguishes it from the other requests. Request bodies and headers are often good places to start. Additionally, you can use req.alias to conditionally assign an alias if you need to wait for that specific call.
cy.intercept('/foo', (req) => {
if (req.body.bar === true) { // or whatever logic indicates the call you want to intercept
req.alias = 'baz'; // conditionally assign alias
req.reply({foo: 'bar'}); // mock response
} else {
req.continue(); // do not mock response
}
});
cy.get('something')
.click()
.wait('#baz'); // waits for your specific 'baz' call to happen.

React.js: Accessing the Retry-After header in an API response

I'm a newer programmer using React.js and the Spotify API for a music app. I’m trying to access the Retry-After header in a 429 rate-limiting error response (Spotify docs here). This is my code currently, which I loosely copied from this article.
async getArtistArt (artistID) {
let url = `https://api.spotify.com/v1/artists?ids=${artistID}`
let response = await fetch(url, {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('accessToken')
},
});
let data = await response.json();
if (Object.keys(data)[0] === "error" && data.error.status === 429) { // Handle rate limiting
console.log('Error!');
console.log(data);
for (var pair of data.headers.entries()) {
console.log(pair);
console.log(pair[0]);
}
}
return data;
}
This is what I see in the console:
screenshot here
console.log('Error!'); // Logs 'Error!'
console.log(data); // Logs error object, but not the header
console.log(pair) // Error that says 'Uncaught (in promise) TypeError: Cannot read property 'entries' of undefined'
I've tried not putting the response into json but that seemed to have no effect.
I've tried to avoid try/catch error statements as I’ve heard they’re somewhat outdated and not usually recommended, but would I need them to access the response header?
I would be grateful for any advice. A big thank you in advance!
I think I found where your error is. The code is almost correct except that you are checking for the headers in the json of the response.
Try to do for (var pair of response.headers.entries()) {...}, so that you are taking the response rather than its json content.

My fetch doesn't upload the JSON string, I can't see the error in my code

I'm using Slim v4 for my REST API for testing purposes.
I want to fetch a JSON Data string to my REST API for saving some events.
public async callSaveEvent(event: EventList) {
let url: string = config.basePath + "eventList/saveEventList";
console.table(JSON.stringify(event));
await fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event })
}).then(response => {
if (!response.ok) {
throw new Error("Something is bad");
}
}).catch(error => {
console.error("Das ist passiert!: ", error);
});
}
This is my current Code. If I use the fetch.options.mode == "cors", I recieve in Slim that this Method is not allowed. Method is OPTIONS instead of POST. Because of this I using mode == "no-cors".
$param = $req->getParsedBody();
$param_ = $param;
$resp->getBody()->write($param);
return $resp;
}
This is my Backend Code. When I try to read the parsedBody, its just empty.
If I send a request with PostMan its accept the data and I get the data in the $param variable.
Can someone find some errors? I can't find them.

How to make synchronous API call request in react js

i am beginner to react js i am working on a small application which makes api requests frequently. So the problem i am facing is there is a page with form fields prefilled from the db, if the user makes changes to those fields i am posting the new filed values to db. when submit button is clicked saveAndConttinue() is called,from there addNewAddress() is invoked based on the condition. But the problem is the response that i get from addNewAddress has to be used for the next api call in the queue, but it is taking time to get response, and the address_id is having null values for it's post call. is there any way to make synchronous call in react with out using flux/redux for now?
saveAndContinue(e) {
e.preventDefault();
if(this.props.params.delivery === 'home_delivery' && this.state.counter) {
this.addNewAddress();
}
console.log('add id is '+this.state.address_id);
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('token', this.props.params.token);
fd.append('dish_id', this.props.params.dish_id);
fd.append('address_type', this.props.params.delivery);
fd.append('address_id', this.state.address_id);
fd.append('ordered_units', this.props.params.quantity);
fd.append('total_cost', this.props.params.total_cost);
fd.append('total_service_charge', this.props.params.service_charge);
fd.append('net_amount', this.props.params.net_cost);
fd.append('hub_id', this.props.params.hub_id);
fd.append('delivery_charge', this.props.params.delivery_charge);
fd.append('payment_type', this.state.payment_type);
fd.append('device_type', 'web');
axios.post(myConfig.apiUrl + '/api/foody/orders/purchase' , fd, config)
.then(function(response){
if(response.data.success) {
console.log(response);
browserHistory.push('/order_confirmed/');
} else {
console.log(response);
//alert(response.data.message)
}
});
}
addNewAddress() {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
if(this.props.params.user_type === 'new') {
fd.append('type', 'PRIMARY');
}
fd.append('token', this.props.params.token);
fd.append('block', this.state.block);
fd.append('door_num', this.state.door_num);
fd.append('address', this.props.params.address);
fd.append('locality', this.props.params.locality);
fd.append('landmark', this.props.params.landmark);
fd.append('hub_id', this.props.params.hub_id);
axios.post(myConfig.apiUrl + '/api/user/add-address' , fd, config)
.then(function(response){
this.setState({address_id: response.data.data['id']});
console.log(this.state.address_id);
}.bind(this));
}
You're going to have to call the next request in the queue after addNewAddress() in the returned promise of addNewAddress():
addNewAddress() {
axios.post()...
.then(function (response) {
// Set state
this.setState({address_id: response.data.data['id']});
// [Call next API request here]
// ...
})
}
Doing synchronous calls are always a bad idea, I personally would advise against it and just do the next call in the returned promise as shown above.

Resources