N
Nuxt2mo ago
Garzec

server API endpoint 404 Not found

Given a basic contact page inside ./app/pages/contact.vue calling a submit event handler like this
// using the Nuxt UI form
async function onSubmit(event: FormSubmitEvent<Contact>) {
await $fetch('/api/contact', {
method: 'POST',
body: event.data,
});

// clear form
// add a success toast
}
// using the Nuxt UI form
async function onSubmit(event: FormSubmitEvent<Contact>) {
await $fetch('/api/contact', {
method: 'POST',
body: event.data,
});

// clear form
// add a success toast
}
For the API route I created a file at ./app/server/api/contact.post.ts with
// ... imports

const runtimeConfig = useRuntimeConfig()

type Contact = z.infer<typeof contactSchema>
const resend = new Resend(runtimeConfig.resendAPIKey)

export default defineEventHandler(async (event) => {
try {
const { customerRequestRecipientEmail } = runtimeConfig;

if (!customerRequestRecipientEmail) {
sendError(event, createError({
statusCode: 400,
statusMessage: '...',
}))

return
}

const body = await readBody<Contact>(event);

const createEmailResponse = await resend.emails.send({
from: body.mail,
to: [customerRequestRecipientEmail],
subject: '...',
text: JSON.stringify(body) // good enough
});

if (!createEmailResponse.error) {
return
}

sendError(event, createError({
statusCode: 500,
statusMessage: createEmailResponse.error.message,
}))
} catch (error) {
// send 500 error
}
});
// ... imports

const runtimeConfig = useRuntimeConfig()

type Contact = z.infer<typeof contactSchema>
const resend = new Resend(runtimeConfig.resendAPIKey)

export default defineEventHandler(async (event) => {
try {
const { customerRequestRecipientEmail } = runtimeConfig;

if (!customerRequestRecipientEmail) {
sendError(event, createError({
statusCode: 400,
statusMessage: '...',
}))

return
}

const body = await readBody<Contact>(event);

const createEmailResponse = await resend.emails.send({
from: body.mail,
to: [customerRequestRecipientEmail],
subject: '...',
text: JSON.stringify(body) // good enough
});

if (!createEmailResponse.error) {
return
}

sendError(event, createError({
statusCode: 500,
statusMessage: createEmailResponse.error.message,
}))
} catch (error) {
// send 500 error
}
});
Unfortunately I'm getting the warning
WARN [Vue Router warn]: No match found for location with path "/api/contact"
and response
{
"error": true,
"url": "http://localhost:3000/api/contact",
"statusCode": 404,
"statusMessage": "Page not found: /api/contact",
"message": "Page not found: /api/contact",
"data": {
"path": "/api/contact"
}
}
{
"error": true,
"url": "http://localhost:3000/api/contact",
"statusCode": 404,
"statusMessage": "Page not found: /api/contact",
"message": "Page not found: /api/contact",
"data": {
"path": "/api/contact"
}
}
Do you know what's wrong or missing? Please let me know if my code is garbage I'm new to Nuxt 🙂 Unfortunately the sandbox works ... dunno why https://stackblitz.com/edit/github-wb9hec5y?file=app%2Fserver%2Fapi%2Fcontact.post.ts
StackBlitz
Nuxt Server API Call Reproduction - StackBlitz
A Vue.js project based on nuxt, vue and vue-router
3 Replies
kapa.ai
kapa.ai2mo ago
Hi @Garzec. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Your code for the API route and the way you are calling it from the form look correct for a Nuxt 3/4 project. The file naming convention (contact.post.ts) and the use of $fetch with a POST method are both standard and should work as expected, registering the endpoint at /api/contact. Common reasons for a 404 on /api/contact in Nuxt: 1. File Location:
The API file must be in the correct directory. For Nuxt 3/4, it should be at ./server/api/contact.post.ts (not ./app/server/api/contact.post.ts). The server/ directory should be at the root of your project, not inside app/.
See directory structure and API route registration. 2. Hot Module Replacement (HMR) Delay:
If you added the file while the dev server was running, sometimes Nuxt's HMR doesn't pick up new files immediately. Try restarting your dev server. 3. Incorrect Route Prefix:
Files in server/api/ are automatically prefixed with /api. If you put your file in server/routes/, it would be available at /contact instead of /api/contact see docs. 4. Typos or File Extension:
Make sure the file is named exactly contact.post.ts (not .js, not .ts.txt, etc.). 5. Nuxt Version:
Ensure you are using Nuxt 3.5 or later for full support of these features. Example working structure:
project-root/
server/
api/
contact.post.ts
app/
pages/
contact.vue
project-root/
server/
api/
contact.post.ts
app/
pages/
contact.vue
Example API handler:
// server/api/contact.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
// server/api/contact.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
Reference Example usage in a page:
// app/pages/contact.vue
<script setup lang="ts">
async function onSubmit(event) {
await $fetch('/api/contact', {
method: 'POST',
body: event.data,
})
}
</script>
// app/pages/contact.vue
<script setup lang="ts">
async function onSubmit(event) {
await $fetch('/api/contact', {
method: 'POST',
body: event.data,
})
}
</script>
Reference Summary:
The most likely issue is the location of your API file. Move contact.post.ts to the root-level server/api/ directory, restart your dev server, and try again. If the file is in the correct place and you still get a 404, double-check for typos and ensure your Nuxt version is up to date. If you follow the structure and conventions from the official docs, your endpoint should be available at /api/contact and work as expected.
See more on API routes and directory structure ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@164630818822684683> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Garzec
GarzecOP2mo ago
my bad, wrong directory for API routes x)

Did you find this page helpful?