I have built a simple Nuxt 3 app and have some custom api endpoints.
I am currently tracking the user query and response statuses to have some insight when something goes wrong and that the app is working as expected.
Running steps
- User will call the endpoint
api/search
with query parameters - Endpoint will set up the process
- Call a third-party api from the server
- If the response is ok, the data will be transformed and returned.
- in case of error, an error message (and status code) will be returned to the user
- Tracking of the user's status code and search query.
Code (simplified)
// api/search.js
const URL = '...'
const buildDataset = items => items.map(...)
// STEP 1: client calls endpoint
export default defineEventHandler(async (event) => {
// STEP 2: setup
const param = useQuery(event)
const query = param.query
const URL = '...'
const res = {
items: [],
status: null,
msg: ''
}
// STEP 3: get data
try {
const response = await fetch(URL, {
headers: {
mode: 'cors',
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'
}
})
if (!response.ok) {
// will this be executed if anything goes wrong? is step 5 here?
res.status = response.status
res.msg = response.statusText
return res
}
// STEP 4: transform and return data
const results = await response.json()
res.status = response.status
res.items = buildDataset(results.collection)
return res
// STEP 5: handle error
} catch (err) {
// res.status = err.response.status // do I got a validate status code here?
res.msg = 'An error occurred'
return res
// STEP 6: track status code and user search query
} finally {
const data = { query, status: res.status }
fetch(SUPABASE_URL, {
method: 'POST',
headers: {
'apikey': SUPABASE_KEY,
'Authorization': 'Bearer ' + SUPABASE_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
})
Questions
- Am I mixing some concepts with my
!response.ok
? I feel like the line don't get triggered if there is an error getting the data. - Will my data be returned to the client immediately when the data arrives, and will my
finally
be executed at the end? or will this block the return of the data? - Where can I set the
error status
in my code to notify the user and my tracking to get insight on what went wrong. - Is there anything else I'm missing?
1 Answer 1
Understanding the Fetch API
The MDN documentation on using the Fetch API answers most of your questions, it's nicely written with many example use cases, I recommend to read it well from top to bottom.
Checking that the fetch was successful
Given this code:
const response = await fetch(URL, ...) if (!response.ok) { // ... }
The if
statement will not be reached if fetch
throws an exception,
which can happen for example when the URL is garbage,
or the server does not respond, or other kind of network errors.
The documentation has a dedicated section on error handling, and recommends to use this pattern:
fetch('flowers.jpg') .then((response) => { if (!response.ok) { throw new Error('Network response was not OK'); } return response.blob(); }) .then((myBlob) => { myImage.src = URL.createObjectURL(myBlob); }) .catch((error) => { console.error('There has been a problem with your fetch operation:', error); });
Behavior of finally and the return statement
The MDN documentation on Promises explains this nicely.
The block of code in finally will always be executed, whether the return
statement is reached or an exception is thrown.
For example:
const result = await new Promise(resolve => resolve("success"))
.finally(() => console.log("finally in case of success"))
console.log("result =", result)
await new Promise((resolve, reject) => reject(new Error("failure")))
.catch(err => console.log("failure happened"))
.finally(() => console.log("finally in case of failure"))
await new Promise((resolve, reject) => reject(new Error("failure")))
.finally(() => console.log("finally in case of failure without catching"))
The output of the above code will be:
finally in case of success result = success failure happened finally in case of failure finally in case of failure without catching file:///path/to/example.js:50 await new Promise((resolve, reject) => reject(new Error("failure"))) ^ Error: failure at file:///path/to/example.js:50:47 at new Promise (<anonymous>) at file:///path/to/example.js:50:7 at processTicksAndRejections (node:internal/process/task_queues:94:5)
Setting error status, notifying the user
Where can I set the error status in my code to notify the user and my tracking to get insight on what went wrong.
Follow the example in the MDN docs:
- Chain the
fetch
to a.then(...)
, which checks theresponse.ok
flag and returnsresponse.json()
if ok, - Chain another
.then(...)
, which will receive the json from the previous one, handle it, and notify the user of success. - Chain a
.catch(...)
to handle exceptions that the previous calls may have thrown, for example about network errors, or whenresponse.ok
was not true. Notify the user of failure. - Chain a
.finally(...)
to do cleanup that must always be performed, regardless of successful processing of the response or failures.
-
\$\begingroup\$ Thank you for the quick response. A colleague told me not to use
then()
and useasync/await
instead. I will read the MDN article and get back to you. \$\endgroup\$wittgenstein– wittgenstein2022年09月07日 10:06:11 +00:00Commented Sep 7, 2022 at 10:06 -
\$\begingroup\$ Hi @wittgenstein, I don't understand the "instead" part. I think one does not exclude the other, you can use them together. It would be interesting to know the justification of your coworker, and ideally a link to documentation or more details. \$\endgroup\$janos– janos2022年09月07日 11:02:14 +00:00Commented Sep 7, 2022 at 11:02
-
\$\begingroup\$ it was like a warning not to mix the old fetch styles (
then/catch
) with the modern ones (try/catch
) to keep my script readable. \$\endgroup\$wittgenstein– wittgenstein2022年09月09日 14:45:54 +00:00Commented Sep 9, 2022 at 14:45