Nuxt 3 server/api: Turning Files into HTTP Endpoints with Nitro
In Nuxt 3, files under server/api automatically become HTTP endpoints through Nitro. This guide covers the mechanism, a worked example, runtime limits, and mistakes to avoid.
22 Apr 2026, 10:51 UTC

The useful answer: files under server/api become HTTP endpoints
If you need a backend endpoint in Nuxt 3 but do not want to run a separate API service, create a file under server/api. Nitro, the server engine bundled with Nuxt, maps each file to an HTTP route automatically. You can then call the endpoint from pages with useFetch or $fetch without configuring a proxy.
How Nitro maps files to routes
At build time, Nitro scans the server/api and server/routes directories. It wraps each file's default export with defineEventHandler and registers the resulting handler under a path that mirrors the file name. A file at server/api/hello.ts becomes the route /api/hello. Nested directories and dynamic segments follow the same convention: server/api/users/[id].ts maps to /api/users/:id.
H3, the minimal HTTP framework that powers Nitro, provides the event handler pattern and utilities such as readBody and getRouterParam. Nuxt auto-imports these helpers, so explicit import statements are usually unnecessary.
A minimal handler
Create server/api/hello.ts with the following content:
export default defineEventHandler((event) => {
return { message: 'Hello from Nuxt' }
})
The handler runs only on the server. When a page requests this endpoint during server-side rendering (SSR), the JSON response is serialized into the initial HTML payload so the client can hydrate—take over the rendered markup—without a second network request.
Calling it from a page
Inside a page component, fetch the endpoint like this:
<script setup>
const { data } = await useFetch('/api/hello')
</script>
<template>
<div>{{ data?.message }}</div>
</template>
useFetch suspends during SSR, so the rendered HTML already contains the greeting string.
Limits: runtime targets, serialization, and static export
Server routes execute inside the Nitro runtime, not in the browser. On a standard Node.js deployment, Node built-in modules such as fs or crypto are available. Edge runtimes—such as Cloudflare Workers or Vercel Edge—restrict many Node APIs and some H3 utilities, so test against your actual target platform before relying on them.
Responses must be serializable. Returning a Date, a Map, a class instance, or a function will either throw or produce unexpected output. Return plain objects, arrays, strings, numbers, booleans, or null.
If callers outside your Nuxt origin will hit the endpoint, you must handle CORS—the browser security policy that controls cross-origin requests—explicitly. Nitro does not add permissive CORS headers by default.
Server routes are not included in a fully static export. Presets such as static generate only client assets; you need a Node server, serverless, or edge target for server/api files to be served.
Common mistakes that break server routes
Leaking secrets through runtimeConfig. Values placed in nuxt.config runtimeConfig can be exposed to the client bundle unless they are properly isolated as server-only. Because the isolation pattern has changed across Nuxt 3 minor versions, verify the approach for your installed version before storing tokens or keys there.
Returning non-serializable objects. A handler that returns a database row containing a Date or a Buffer may fail during SSR serialization. Convert these to strings or ISO timestamps before returning.
Importing server files into client components. Files under server/api are compiled into the server bundle only. Attempting to import them from a client-side component or composable will fail because the module does not exist in the browser bundle.
Calling server-only utilities on the client. Functions such as readBody or getRouterParam depend on the incoming request event. Calling them outside a server handler produces errors. Keep request parsing inside server/api files.
Verifying the route before wiring it into UI
Before integrating an endpoint into a page, confirm it returns the expected payload. From your project root, run:
npx nuxi dev
No elevated permissions are required. Once the dev server is running, open http://localhost:3000/api/hello in a browser. You should see the JSON object returned by the handler. If the route returns HTML instead, the file is likely outside server/api or the default export is missing.
To inspect how the route is compiled for production, run npx nuxi build and examine the .output directory. The generated server entry will show how Nitro bundled the handler for your configured preset.
Finally, load a page that uses useFetch('/api/hello') and open your browser's Network tab. During SSR, the request should appear as a server-side fetch with the response inlined into the initial document payload, not as a separate client-side XHR after hydration.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.