testing fastapi response to next.js with fetch, promise stuck in pending? - javascript

my fetch is stuck in pending when I query a fastapi endpoint in local dev.
followed this blog and a few others - https://damaris-goebel.medium.com/promise-pending-60482132574d
Using this fetch code (having simplified it drastically just to get a simple solution working)
function fastapiRequest(path) {
return fetch(`${path}`)
.then((response) => {
return response;
}
);
into a constant variable i.e.,
const xxxx = fastapiRequest(
`http://0.0.0.0:8008/xcascasc/Dexaa/Emo.json?Magic=Dexxaa&emotion=expressions`
);
Ideally I want to use UseSWR to do this as I'm using next.js, but first of all, just need it to work :)
A postman query like this works fine to return a value
curl --location --request GET 'http://0.0.0.0:8008/xcaxc/dexxa/emo.json?analysis=statistical&substance=dexxa&emo=powa' \
--header 'x_token: 13wdxaxacxasdc1'
the value is left like this in console.log
data show here? Promise {<pending>}
With the initial response being
Response {type: 'cors', url: 'url', redirected: false, status: 200, ok: true, …}
Update based on answers.
Using each of the proposed answers, I am still not getting the data returned appropriately. i.e.,
function fastApiRequest(path) {
console.log("really begins here");
return fetch(`${path}`, { mode: 'cors' })
.then((response) => {
console.log('response', response);
return response.json();
})
.catch((err) => {
throw err;
});
}
async function test() {
console.log('begins');
return await fastApiRequest(
`http://0.0.0.0:8008/xxxx/dex/adea.json?deaed=adedea&adee=deaed&adeada=adeeda`
);
}
const ansa = test();
Is giving a response of pending at the moment.
The backend is built with fastapi, with these CORS, I'm wondering if I need to give it more time to get the data? (postman works fine :/)
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
origins = [
"http://moodmap.app",
"http://localhost:3000/dashboard/MoodMap",
"http://localhost:3000",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
max_age=3600,
)
I am running the fastapi code in a docker container as well btw

As per Documentation
The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.
.json() is an async method (it returns a Promise itself), so you have to assign the parsed value in the next .then(). So your code can be changed like this.
function fastApiRequest(path) {
let res;
fetch(`${path}`)
.then((response) => response.json())
.then((data) => (res = data))
.then(() => console.log(res));
return res;
}
response = fastApiRequest('https://proton.api.atomicassets.io/atomicassets/v1/accounts?limit=10');
console.log('response')
If you want to use async/await approach, below is the code.
async function fastApiRequest(path) {
try {
const response = await fetch(path);
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}
async function test() {
console.log(await fastApiRequest('https://proton.api.atomicassets.io/atomicassets/v1/accounts?limit=10'))
}
test()

first you need to parse the response into json if it's a json API.
function fastapiRequest(path) {
return fetch(`${path}`)
.then((response) => {
return response.json();
});
}
you need to 'await' for the rsponse
you need to write the below code in an async function
const xxxx = await fastapiRequest(
`http://0.0.0.0:8008/xcascasc/Dexaa/Emo.json?Magic=Dexxaa&emotion=expressions`
);
When you make an http request using fetch in javascript it will return a Promise, it's not stuck it's just need to be resloved, you can resolve it just like the above code with async await, or you can use the .then(() => { /* code... */ }) function, you can also use .catch(() => { /* handle error... */ }) function to handle errors.

In Your curl you use x_token as header variable, if it's required you need to pass a header with your path too. All other answers are valid too.

Related

being able to use data with fetch api request and error checking

Trying to make an API request with error checking and then be able to use the data from the API outside the async function. This is returning a pending promise right now. Not sure how get useable data out of the API that I can then further use throughout the program. Suggestions?
const fetch = require('node-fetch')
const url = 'https://employeedetails.free.beeceptor.com/my/api/path'
async function getData(url){
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error ("why did I become a software engineer?");
}
else {
return await response.json();
}
}
catch (error) {
console.log(error);
}
}
const data = getData(url);
console.log(data);
async functions return a promise. To get the usable data you will have to either await getData(url) (create another async function for this) or in your example you can write:
getData(url).then(data => console.log(data);
I think the problem here is that the async function is returning a promise so you should add another asyn function to get that data, or maybe you should add a global variable before the function and then change its value

JavaScript: Is there a way to detect if a response contains error messages in json format when it is not ok

I am writing a simple wrapper around fetch.
async function apiCall(
endpoint: string,
{
data,
headers: customHeaders,
...customConfig
}: { data?: Object; headers?: Object } = {}
) {
const config = {
method: data ? 'POST' : 'GET',
body: data ? JSON.stringify(data) : undefined,
headers: {
'content-type': data ? 'application/json' : undefined,
...customHeaders,
},
...customConfig,
}
return fetch(endpoint, config as any).then(async (response) => {
if (response.ok) {
const json = await response.json() // 🤔
return json
} else {
// 👇 🚨 what if `response` contains error messages in json format?
return Promise.reject(new Error('Unknown Error'))
}
})
}
it works fine. The problem is with this snippet
return fetch(endpoint, config as any).then(async (response) => {
if (response.ok) {
const json = await response.json()
return json
} else {
// 👇 🚨 what if `response` contains error messages in json format?
return Promise.reject(new Error('Unknown Error'))
}
})
if response is not ok, it rejects with a generic Error. This is because by default, window.fetch will only reject a promise if the actual network request failed. But the issue is that, even if response is not ok, it might still be able to have error messages in json format. This depends on the backend implementation details but sometimes you are able to get the error messages in the response body by response.json(). Now this use case is not covered in the wrapper I built.
So I wonder how I am going to be able to account for that? I guess you can do something like
fetch(endpoint, config as any).then(async (response) => {
if (response.ok) {
const json = await response.json()
return json
} else {
try {
const json = await response.json()
return Promise.reject(json)
} catch {
return Promise.reject(new Error('Unknown Error'))
}
}
})
but I wonder if there is some more elegant way to do that?
Lastly, I am very aware of libraries like Axios. I built this partly to satisfy my intellectual curiosity.
Btw, a lightly unrelated question but I wonder if these two are equivalent
if (response.ok) {
const json = await response.json()
return json
}
if (response.ok) {
return response.json()
}
Someone flagged my question as a duplicate of this question. In fact they are not the same. I did not make the same assumption as that question did about the API call returning JSON data both on success and on failure. My question is about exactly how we should do in cases where we cannot make such an assumption.
That the response is not ok doesn't prevent you from consuming its body as JSON so your last snippet is indeed how it should be handled.
Now you ask for something "more elegant", well it may not be more elegant but a less redundant way to write the same would be:
fetch(endpoint, config as any).then(async (response) => {
if (response.ok) {
return response.json(); // there is no need to await the return value
}
else {
const err_message = await response.json() // either we have a message
.catch( () => new Error( "Unknown Error" ) ); // or we make one
return Promise.reject( err_message );
}
})
And regarding the last question, yes both are equivalent, the await version doing one more round-trip through the microtask-queue and making your code a bit longer, but for all that matters we could say they are the same.
I'd simplify Kaiido even further, fix some bugs, and add a custom error handler.
class ApiCallError extends Error {
response: Response;
body: any;
httpStatus: number;
constructor(response, body) {
super('HTTP error: ' + response.status);
this.httpStatus = response.status;
this.response = response;
this.body = body;
}
}
async function apiCall(endpoint: string, options?: any) {
const config = {
// do your magic here
}
const response = await fetch(endpoint, config);
if (!response.ok) {
throw new ApiCallError(response, await response.json());
}
return response.json();
}
Changes:
You don't need to catch errors if you're just going to throw again.
You don't need .then() if you support await, and this will make your code a lot simpler.
There's really no point in Promise.resolve and Promise.reject, you can just return or throw.
You should not return plain errors, you should throw them (or make sure they are wrapped in a rejecting promise)
Even though in javascript you can 'throw anything' including strings and arbitrary objects, it's better to throw something that is or extends Error, because this will give you a stack trace.
Making all the error information available on the custom error class provides everything a user of apiCall could possibly need.

How to process fetch() response and still work asynchronously

I have a JS program that does a whole lot of fetch() calls to a specific API. I want to abstract all the fetch() calls into a single class called "apiService" so my code will be more readable. I want the apiService to apply some intelligence and then return responses to the caller, in the following ways:
- apiService should check the response to see if there are errors present, which it must always process in the same way.
- fetch() will sometimes receive a "res" that is raw data and should be used as is, and sometimes it'll received json that needs a .then(res => res.json().then(res applied so it can return an object.
So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.
Anyone know how I could structure the apiService function to process the html responses but also return asynchronously i.e. the calling function would receive the result object after the error checking etc.
So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.
This gives me the feeling that you might be misunderstanding promises a little bit. Take this example:
const doAsyncWork = () => fetch('somewhere').then(() => console.log('fetch is complete'))
// use the above function
doAsyncWork().then(() => console.log('used the fetching function'))
The output of the above code will be
fetch is complete
used the fetching function
As you can see, by chaining then after the fetch call, you are actually returning the result of then, and not fetch. Another way to think of it is, what are you actually returning if you called
const result = a().b().c() // we are really returning the result of `c()` here.
With the above in mind you can most definitely do something like:
const apiCall = loc => fetch(loc).then(res => {
// do things with your response
return res
})
apiCall('someEndpoint').then(finalRes => {
console.log('this is called after fetch completed and response processed')
})
There is a nice article about it here called "Synchronous" fetch with async/await that will break it down for you in pieces.
In Short:
You can use await when using fetch():
const response = await fetch('https://api.com/values/1');
const json = await response.json();
console.log(json);
First we wait for the request to finish, Then we can wait for it to complete (or fail) and then pass the result to the json variable.
The complete example would be to use async, because `await won't work without it:
const request = async () => {
const response = await fetch('https://api.com/values/1');
const json = await response.json();
console.log(json);
}
request();
I thinks you can fulfill your requirement using Promise.all()
Here is an example for you.
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
For more info you can refer:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
You can use a library called axios instead of worrying about promises and data formats yourself.
However, if you still want to do it, use the following way.
you can use a method to create promises like this.
makeRequest(url, requestData) {
const response = await fetch(url, requestData)
.then(response => { console.info('network request successful to', url); return response.json() })
.then(json => {
console.info('response received for request', url, requestData, json)
return json;
})
.catch(e => {
console.error('error at request', url, requestData, e);
return e
});
return response;
}
and use the promises like this
makeRequest('someurl', {
method: 'GET'
}).then(response=>{/*Your logic*/}).catch(error=>{/*Your logic*/});

fetch response.json() and response.status

Is this the only way to use the body.json() and also get the status code?
let status;
return fetch(url)
.then((response => {
status = response.status;
return response.json()
})
.then(response => {
return {
response: response,
status: status
}
});
This doesn't work as it returns a promise in the response field:
.then((response)=> {return {response: response.json(), status: response.status}})
Your status is not visible in the second then. You can just get the two properties in the single then.
json() returns a new Promise to you, so you need to create your object inside the then of the result of that function. If you return a Promise from a function, it will be fulfilled and will return the result of the fulfillment - in our case the object.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(r => r.json().then(data => ({status: r.status, body: data})))
.then(obj => console.log(obj));
The .json method returns a promise, not the parsed value itself. If you want to access both the response and the parsed value in the same callback, you'll need to use nested functions like this:
fetch(url)
.then(response => {
response.json().then(parsedValue => {
// code that can access both here
})
});
Alternatively, you can use await inside an asynchronous function to eliminate the need for callbacks.
const response = await fetch(url);
const parsedValue = await response.json();
// code that can access both here
Of course, you'll want to check for errors, either with a .catch(...) call on a Promise or with a try...catch block in an async function. You could make a function that handles JSON and error cases, and then reuse it for all fetches. For example, something like this:
function handle(response) {
if (response.ok) {
return response.json().then(parsedValue => {
// the status was ok and the body could be parsed
return Promise.resolve({ response, parsedValue });
}).catch(err => {
// the status was ok but the body was empty or not JSON
return Promise.resolve({ response });
});
} else {
return response.json().catch(err => {
// the status was not ok and the body was unobtainable/empty/not JSON
throw new Error(response.statusText);
}).then(parsedValue => {
// the status was not ok and the body was JSON
throw new Error(parsedValue.error.message); // assuming an error message is returned by our REST API
});
}
}
I don't think it's the best design pattern, but hopefully this clarifies how the fetch API works.
PS: I avoided naming any variable or property json since that is the name of the text format. Once it's been parsed, it is no longer JSON.
Using two 'then's seem unnecessary to me.
async/await could get the job done pretty easily.
fetch('http://test.com/getData')
.then( async (response) => {
// get json response here
let data = await response.json();
if(data.status === 200){
// Process data here
}else{
// Rest of status codes (400,500,303), can be handled here appropriately
}
})
.catch((err) => {
console.log(err);
})
Did you try this?
return fetch(url)
.then((r)=> {return {response: r.json(), status: r.status}})
I think the cleanest way is to create a Promise.all() with the pieces you need.
.then(response => Promise.all([Promise.resolve(response.ok), response.text()]))
Which can be written shorter as
.then(response => Promise.all([response.ok, response.text()]))
The promise returns an array with all of the results
.then(data => ({ status: data[0], response: data[1] }))

react native fetch not calling then or catch

I am using fetch to make some API calls in react-native, sometimes randomly the fetch does not fire requests to server and my then or except blocks are not called. This happens randomly, I think there might be a race condition or something similar. After failing requests once like this, the requests to same API never get fired till I reload the app. Any ideas how to trace reason behind this. The code I used is below.
const host = liveBaseHost;
const url = `${host}${route}?observer_id=${user._id}`;
let options = Object.assign({
method: verb
}, params
? {
body: JSON.stringify(params)
}
: null);
options.headers = NimbusApi.headers(user)
return fetch(url, options).then(resp => {
let json = resp.json();
if (resp.ok) {
return json
}
return json.then(err => {
throw err
});
}).then(json => json);
Fetch might be throwing an error and you have not added the catch block. Try this:
return fetch(url, options)
.then((resp) => {
if (resp.ok) {
return resp.json()
.then((responseData) => {
return responseData;
});
}
return resp.json()
.then((error) => {
return Promise.reject(error);
});
})
.catch(err => {/* catch the error here */});
Remember that Promises usually have this format:
promise(params)
.then(resp => { /* This callback is called is promise is resolved */ },
cause => {/* This callback is called if primise is rejected */})
.catch(error => { /* This callback is called if an unmanaged error is thrown */ });
I'm using it in this way because I faced the same problem before.
Let me know if it helps to you.
Wrap your fetch in a try-catch:
let res;
try {
res = fetch();
} catch(err) {
console.error('err.message:', err.message);
}
If you are seeing "network failure error" it is either CORS or the really funny one, but it got me in the past, check that you are not in Airplane Mode.
I got stuck into this too, api call is neither going into then nor into catch. Make sure your phone and development code is connected to same Internet network, That worked out for me.

Categories