Is there a way to NOT show an HTML error from an API?

The recommended way of handling errors in Nitro/API-land is to use createError. When hit directly, these show a full html error page, or if hit with accept: application/json, show a json payload with stack info. I'd like to return a simple message on-error vs all that. It would seem the only option I have is to use setResponseStatus and return whatever directly from the api vs passing something to createError. IMHO, no matter what, an api shouldn't be returning full page html errors.
4 Replies
dmarr
dmarr4mo ago
I just put this together for showing a toast during a fetch error. You would still need to handle the fetch calls with try/catch, as i do in components/SomeComp.vue
dmarr
dmarr4mo ago
GitHub
GitHub - marr/nuxt-error-toast: Example showing how to display a to...
Example showing how to display a toast when there is a fetch error. - marr/nuxt-error-toast
dmarr
dmarr4mo ago
It's using radix-ui for the toast.. i never see examples of showing toasts like this with errors, so im not sure if there is a better approach
Mads
Mads4mo ago
Hi Josh. I recently went through same frustration. Here’s how I handle it:
import { and, eq } from 'drizzle-orm'
import { Err, Ok } from 'ts-results-es'
import { used_cars } from '@/../server/database/core-schema'

export default defineEventHandler(async (event) => {
if (!event.context.session) {
setResponseStatus(event, 401, 'You are not logged in')
return new Err({ _tag: 'UnauthorizedError', message: 'You are not logged in' } as const)
}
if (!event.context.user) {
setResponseStatus(event, 401, 'Could not find user')
return new Err({ _tag: 'UnauthorizedError', message: 'Could not find user' } as const)
}

const user = event.context.user

if (!user.selected_company_id) {
setResponseStatus(event, 401, 'User has no selected company')
return new Err({ _tag: 'NoSelectedCompanyError', message: 'User has no selected company' } as const)
}

try {
const cars = await useDB().query.used_cars.findMany({
where: and(
eq(used_cars.company_id, user.selected_company_id),
),
}).execute()

return new Ok({
_tag: 'Success',
message: 'Used cars found',
used_cars: cars,
} as const)
}
catch (error) {
console.error(error)
setResponseStatus(event, 500, 'Failed to fetch used cars')
return new Err({ _tag: 'DatabaseFetchError', message: 'Failed to fetch used cars', error } as const)
}
})
import { and, eq } from 'drizzle-orm'
import { Err, Ok } from 'ts-results-es'
import { used_cars } from '@/../server/database/core-schema'

export default defineEventHandler(async (event) => {
if (!event.context.session) {
setResponseStatus(event, 401, 'You are not logged in')
return new Err({ _tag: 'UnauthorizedError', message: 'You are not logged in' } as const)
}
if (!event.context.user) {
setResponseStatus(event, 401, 'Could not find user')
return new Err({ _tag: 'UnauthorizedError', message: 'Could not find user' } as const)
}

const user = event.context.user

if (!user.selected_company_id) {
setResponseStatus(event, 401, 'User has no selected company')
return new Err({ _tag: 'NoSelectedCompanyError', message: 'User has no selected company' } as const)
}

try {
const cars = await useDB().query.used_cars.findMany({
where: and(
eq(used_cars.company_id, user.selected_company_id),
),
}).execute()

return new Ok({
_tag: 'Success',
message: 'Used cars found',
used_cars: cars,
} as const)
}
catch (error) {
console.error(error)
setResponseStatus(event, 500, 'Failed to fetch used cars')
return new Err({ _tag: 'DatabaseFetchError', message: 'Failed to fetch used cars', error } as const)
}
})
Then I use it something like this
try {
const response = await $fetch('/api/used-cars/all', {
method: 'GET',
ignoreResponseError: true,
})

if (response && 'value' in response) {
return response.value.used_cars
}
else {
if (response.error._tag === 'UnauthorizedError') {
toast.add({ title: 'Du er ikke autoriseret', description: 'Du er måske ikke logget ind, eller har ikke autorisation til at oprette en prisprotokol', color: 'red' })
}
try {
const response = await $fetch('/api/used-cars/all', {
method: 'GET',
ignoreResponseError: true,
})

if (response && 'value' in response) {
return response.value.used_cars
}
else {
if (response.error._tag === 'UnauthorizedError') {
toast.add({ title: 'Du er ikke autoriseret', description: 'Du er måske ikke logget ind, eller har ikke autorisation til at oprette en prisprotokol', color: 'red' })
}
But yes, as you mentioned I do set the responseStatus. The createError is a suboptimal solution imo.
Want results from more Discord servers?
Add your server