N
Nuxt5mo ago
Kyllian

$fetch catches error, but not able to read body

Hi, I must be missing something. I'm attempting to get a $fetch error and it's JSON body, but it simply returns 'POST (url) 422 Unprocessable Entity)
async function onSubmit(event: FormSubmitEvent<any>) {
form.value.clear()
try {
const response = await $fetch('/api/skin/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: {
email: state.email,
password: state.password
}
});

if (!response.ok) {
const responseBody = await response.json(); // read the response body
console.error("the error", responseBody);
return;
}

console.log("ok!");
} catch (err) {
console.error("the error", err);
}
}
async function onSubmit(event: FormSubmitEvent<any>) {
form.value.clear()
try {
const response = await $fetch('/api/skin/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: {
email: state.email,
password: state.password
}
});

if (!response.ok) {
const responseBody = await response.json(); // read the response body
console.error("the error", responseBody);
return;
}

console.log("ok!");
} catch (err) {
console.error("the error", err);
}
}
No description
17 Replies
Kyllian
Kyllian5mo ago
even with useFetch it somehow returns this, and the data maintains empty within the 'network' tab I can see it just fine
manniL
manniL5mo ago
@Kyllian $fetch does already to the conversion for your 😉 no need to check for response.ok and to convert to JSON as with the plain fetch api
manniL
manniL5mo ago
GitHub
GitHub - unjs/ofetch: 😱 A better fetch API. Works on node, browser ...
😱 A better fetch API. Works on node, browser and workers. - GitHub - unjs/ofetch: 😱 A better fetch API. Works on node, browser and workers.
Kyllian
Kyllian5mo ago
ah there is an ignoreResponseError, damn that's so easy, thanks man
manniL
manniL5mo ago
no problem 👍
Kyllian
Kyllian5mo ago
it's so easy you overlook it haha
fmeyjr
fmeyjr5mo ago
I have a similar problem, that the body is undefined on the server. In my store, I have the following function:
const addOffice = async () => {
if (!state.city || !state.name || !state.street || !state.zip) {
return;
}

// TODO put this in util
// The first letter of Name, Street and city need to be uppercase
state.name = state.name.charAt(0).toUpperCase() + state.name.slice(1);
state.street = state.street.charAt(0).toUpperCase() + state.street.slice(1);
state.city = state.city.charAt(0).toUpperCase() + state.city.slice(1);

const reqBody = {
name: state.name,
street: state.street,
city: state.city,
zip: state.zip,
organizationId: organizationID,
}
console.log("Add office: reqBody", reqBody);

try {
const office = await $fetch("/api/offices", {
method: 'post',
body: reqBody,
});
} catch (error) {
toast.add({ title: `Fehler beim Erstellen von Büro.`, color: "red" });
return;
}
...
const addOffice = async () => {
if (!state.city || !state.name || !state.street || !state.zip) {
return;
}

// TODO put this in util
// The first letter of Name, Street and city need to be uppercase
state.name = state.name.charAt(0).toUpperCase() + state.name.slice(1);
state.street = state.street.charAt(0).toUpperCase() + state.street.slice(1);
state.city = state.city.charAt(0).toUpperCase() + state.city.slice(1);

const reqBody = {
name: state.name,
street: state.street,
city: state.city,
zip: state.zip,
organizationId: organizationID,
}
console.log("Add office: reqBody", reqBody);

try {
const office = await $fetch("/api/offices", {
method: 'post',
body: reqBody,
});
} catch (error) {
toast.add({ title: `Fehler beim Erstellen von Büro.`, color: "red" });
return;
}
...
and on my server route server/api/offices/index.post.ts, i have the function:
export default defineEventHandler(async (event) => {
const client = await serverSupabaseClient<DB>(event);
const user = await serverSupabaseUser(event);

// Throw error if no user is found
if (!user) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}

// Get all offices from the request body
const { body } = await readBody(event);
console.log("post office readBody", body);

// Throw error if there is no body
if (!body) {
throw createError({ statusCode: 400, statusMessage: "Bad Request. No body found!" });
}
...
export default defineEventHandler(async (event) => {
const client = await serverSupabaseClient<DB>(event);
const user = await serverSupabaseUser(event);

// Throw error if no user is found
if (!user) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}

// Get all offices from the request body
const { body } = await readBody(event);
console.log("post office readBody", body);

// Throw error if there is no body
if (!body) {
throw createError({ statusCode: 400, statusMessage: "Bad Request. No body found!" });
}
...
However, the body is undefined on the server. What do i miss?
manniL
manniL5mo ago
don't destructure 🙂 should be const body not const { body }
fmeyjr
fmeyjr5mo ago
Its absolutely funny how its always the little things. Thanks!
keco39
keco394mo ago
This answer means it's not possible to get the error object from a $fetch request (you have to use ofetch)? I was hoping I didn't have to install another package for that... My use case is the following: there is no up-to-date package for Campaign Monitor (for Nuxt/Vue/Node) so I created a couple of API endpoints to call CM myself (like this: const response: string = await $fetch(https://api.createsend.com/api/v3.3/subscribers/${listid}.json, { method: "POST", body: body, headers: { Authorization: Basic ${authToken} } });). When an error occurs, I only see "400 Bad Request" and a stacktrace but I'm missing the underlying reason (so I have to use tools like PostMan to check what the actual error is).
manniL
manniL4mo ago
ofetch and $fetch are the same ☺️
This answer means it's not possible to get the error object from a $fetch request
No. try/catch it 👍
fmeyjr
fmeyjr4mo ago
Use try and catch
keco39
keco394mo ago
I did do the try catch but the error object looks just like a string... Or can I pass "ignoreResponseError: true" as an option to $fetch (TS isn't recognizing it it seems...). Or read err.data without the ignore option? Because TS also says it does not know .data and I don't really know of which type the error is. This is the whole thing actually...
try {
const body: CampaignMonitorSubscriptionRequest = { EmailAddress: payload.email, Name: payload.name, Resubscribe: true, ConsentToTrack: "Yes" };
const response: string = await $fetch(postUrl, { method: "POST", body: body, headers: { Authorization: `Basic ${authToken}` } });

if (import.meta.dev) {
// eslint-disable-next-line no-console
console.debug("Campaign Monitor POST subscription response", response);
}

return {
success: true,
email: response,
};
}
catch (error) {
// https://www.campaignmonitor.com/api/v3-3/subscribers/#adding-a-subscriber
console.error("Campaign Monitor error:", error);

return {
success: false,
message: "Internal error.",
};
}
try {
const body: CampaignMonitorSubscriptionRequest = { EmailAddress: payload.email, Name: payload.name, Resubscribe: true, ConsentToTrack: "Yes" };
const response: string = await $fetch(postUrl, { method: "POST", body: body, headers: { Authorization: `Basic ${authToken}` } });

if (import.meta.dev) {
// eslint-disable-next-line no-console
console.debug("Campaign Monitor POST subscription response", response);
}

return {
success: true,
email: response,
};
}
catch (error) {
// https://www.campaignmonitor.com/api/v3-3/subscribers/#adding-a-subscriber
console.error("Campaign Monitor error:", error);

return {
success: false,
message: "Internal error.",
};
}
fmeyjr
fmeyjr4mo ago
Throw the error with createError
keco39
keco394mo ago
Instead of the "Internal error", I would like to "personalize" it according to the error message within the 400 response.
fmeyjr
fmeyjr4mo ago
Im on my phone but something like: Catch(e) { Throw createError({ status: 400, statusMessage: ‘error msg: ${e}’ }) I don’t have back ticks here. So obviously use back ticks 😂
keco39
keco394mo ago
Sure 🙂 Gonna try to see if I get more details from the error itself... Not really what I was hoping for 😦 This is what was returned (I put an extra try..catch in my page, calling my own API endpoint):
Campaign Monitor error in page: FetchError: [POST] "/api/newsletters/subscription?listid=...": 500 Campaign Monitor error: FetchError: [POST] "https://api.createsend.com/api/v3.3/subscribers/....json": 400 Bad Request
at async $fetch22 (ofetch.00501375.mjs:261:15)
at async subscribe (newsletters.vue:24:30)
at async updateSubscription (newsletters.vue:55:5)
Campaign Monitor error in page: FetchError: [POST] "/api/newsletters/subscription?listid=...": 500 Campaign Monitor error: FetchError: [POST] "https://api.createsend.com/api/v3.3/subscribers/....json": 400 Bad Request
at async $fetch22 (ofetch.00501375.mjs:261:15)
at async subscribe (newsletters.vue:24:30)
at async updateSubscription (newsletters.vue:55:5)
And this is what I see in Postman (status is 400 Bad Request)...
{
"Code": -213,
"Message": "Please provide a consent to track value."
}
{
"Code": -213,
"Message": "Please provide a consent to track value."
}
Just found this online...
console.error("Campaign Monitor error:", error.response._data);
console.error("Campaign Monitor error:", error.response._data);
Didn't know that was a thing (now I have to find the type of that error :)). Sorry for the question (but maybe the answer can help others).