A Nuxt project with a server/ directory is a full application: routes, middleware, database access, sessions, background tasks and a deployable server, all in one repository and one type system. Fullstack interviews for Nuxt roles test whether you treat that server as a real backend: validated inputs, a data layer, authorisation, caching, operations. The module-author sections explain Nitro from the inside; this section is about building with it.
server/ into a standalone, platform-agnostic server (.output/server) using h3 as the HTTP layer, with presets for Node, Cloudflare Workers, Vercel, Netlify, AWS Lambda, Deno, Bun and static hosting. Nuxt 4 runs Nitro 2 with h3 v1 APIs; Nitro 3 and h3 v2 (web-standard Request/Response) arrive with Nuxt 5 Nuxt 5 , and nuxt/server (from 4.6) is the runtime-agnostic import surface that survives that change.$fetch; schemas in shared/ validate on both sides; one build produces the client and the server. That is the productivity argument for fullstack Nuxt.window, module scope is process scope (shared across requests), connections are pooled and long-lived, secrets come from runtimeConfig, and edge presets restrict Node APIs. Cross-request state pollution and unbounded caches are the classic bugs (memory leaks).| Page | Question it answers |
|---|---|
| Server routes and h3 | "Write an endpoint": params, bodies, validation, headers, streaming, middleware, plugins, context |
| Data layer and databases | Postgres or SQLite, Drizzle or Prisma, migrations, pooling in serverless, N+1, indexes, KV and blobs |
| Auth and sessions in Nuxt | nuxt-auth-utils end to end, OAuth, passkeys, RBAC, SSR forwarding, alternatives |
| Server caching and performance | defineCachedEventHandler, route rules, storage drivers, invalidation, render cost, cold starts |
| Files, realtime and background work | Uploads and presigned URLs, WebSockets and SSE, Nitro tasks, queues, idempotency |
| API design and integration | REST conventions, OpenAPI, tRPC/oRPC/GraphQL trade-offs, BFF, proxies, webhooks, rate limits |
| Deployment and operations | Presets, Docker, Cloudflare and Vercel, env and secrets, health, logging, monitoring, migrations, rollbacks |
The shape of a well-built Nitro backend inside a Nuxt project:
server/
├── api/ thin handlers: validate -> authorise -> call a service -> shape the response
│ ├── products/[id].get.ts
│ └── orders.post.ts
├── routes/ non-/api routes: sitemap.xml, webhooks, redirects, health
├── middleware/ per-request: request id, session -> event.context.user
├── plugins/ startup: db pool, error reporting, scheduled task registration, hooks
├── utils/ auto-imported: useDb(), requireRole(), assertRateLimit(), services/*
├── tasks/ Nitro tasks (cron / on-demand): cleanup, digests, reindex
├── db/ schema.ts, migrations/ (Drizzle)
└── tsconfig.json server project reference
shared/
├── schemas/ zod: one definition for client and server
└── types/ DTOs
import { createOrderSchema } from '#shared/schemas/order'
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event) // who
const input = await readValidatedBody(event, createOrderSchema.parse) // what, validated
const order = await createOrder(useDb(), { userId: user.id, ...input }) // service: the business rule lives here, testable without HTTP
setResponseStatus(event, 201)
setResponseHeader(event, 'location', `/api/orders/${order.id}`)
return toOrderDto(order) // never the raw row
})
A 200-line defineEventHandler that validates, queries, computes and formats cannot be unit-tested without HTTP and cannot be reused from a task or a webhook. Handlers stay thin; services in server/utils or server/services own the rules and take the database as a parameter.
server/ tree for a product you know well; mark which handlers currently contain business logic and which would become services."The server/ directory is a real backend on Nitro: h3 handlers with typed routes, validated inputs, a data layer with Drizzle or Prisma, sessions with nuxt-auth-utils, cached handlers, WebSockets and server-sent events, scheduled tasks, and one build that deploys to Node, Cloudflare, Vercel or a container. The payoff is one type system and one deploy: a handler's return type reaches the page, a shared schema validates both sides, and a small team ships end to end. It is the right choice for a product's own API and for a backend-for-frontend over third-party services; I would separate a service when work is long-running or CPU-heavy, when another team or language owns the domain, or when throughput or compliance demand it, and even then Nitro stays in front as the BFF."
Nothing yet. Notes filed here show up on every page of this section.
Building, versioning and publishing
What nuxt-module-build emits, the package.json fields that decide whether your module resolves, semver for modules, deprecating an option safely, and getting listed on nuxt.com/modules.
Server routes and h3
Everything a Nitro handler can do — routing by file, params, query and body with validation, headers and status, cookies, event.context set in server middleware, plugins and hooks, lazy handlers, streaming responses, in-process $fetch, typed routes — and what changes with h3 v2 and nuxt/server.