Nuxt 4 Server Routes & Nitro: Advanced Patterns and Gotchas
|
Sunday 20th July, 2025
|
12 minutes mins to read
|
Share this blog

Nuxt 4 introduces a new server engine, Nitro, and a powerful server routes system that lets you build full-stack applications with ease. But with great power comes new complexity. This post explores advanced patterns and common pitfalls when working with Nuxt 4 server routes and Nitro.
Dynamic API Endpoints
You can create dynamic API endpoints by adding files to the server/api
directory:
// server/api/posts/[id].ts
export default defineEventHandler(async (event) => {
const id = event.context.params.id;
// Fetch post from database or external API
return { id, title: `Post #${id}` };
});
Gotcha: Params Are Always Strings
All route params are strings. If you expect a number, cast it explicitly:
const id = Number(event.context.params.id);
Middleware and Auth
Nuxt 4 lets you add server middleware for authentication, logging, and more:
// server/middleware/auth.ts
export default defineEventHandler((event) => {
if (!event.context.user) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}
});
Register middleware in your nuxt.config.ts:
export default defineNuxtConfig({
nitro: {
middleware: ["~/server/middleware/auth"],
},
});
Deployment Caveats
- Edge vs Node: Nitro can deploy to many platforms (Vercel, Netlify, Node,
Cloudflare). Some Node APIs (like
fs) are not available on edge platforms. - Cold Starts: Serverless platforms may have cold start delays. Cache responses where possible.
- Environment Variables: Use
useRuntimeConfig()for runtime secrets, not process.env directly.
Best Practices
- Keep server logic in
server/and avoid mixing with client code. - Validate all input in API routes to prevent security issues.
- Use composables for shared logic between server and client.
Example: Secure API with Validation
// server/api/secure-data.ts
import { z } from "zod";
const schema = z.object({ token: z.string().min(10) });
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const result = schema.safeParse(body);
if (!result.success) {
throw createError({ statusCode: 400, statusMessage: "Invalid input" });
}
// ...secure logic
return { ok: true };
});
Wrap-Up
Nuxt 4's server routes and Nitro open up new possibilities for full-stack development. Mastering these features will help you build robust, scalable, and secure applications with the latest Nuxt ecosystem.