Fullstack with Nitro

Fullstack with Nitro

What "fullstack Nuxt" means in 2026 — the server directory as a real backend on Nitro, when it is enough and when a separate service is warranted — and how the pages on routes and h3, databases, auth, server caching, files, realtime and background work, API design and deployment fit together.

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.

Know

  • Nitro is the server engine: it compiles 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.
  • One type system, one deploy: the handler's return type reaches the page through $fetch; schemas in shared/ validate on both sides; one build produces the client and the server. That is the productivity argument for fullstack Nuxt.
  • When Nitro is enough: a product's own API, a backend-for-frontend aggregating third-party services, auth and sessions, webhooks, scheduled tasks, file handling; anything a typical Node service does. When to separate: long-running or CPU-heavy work, a different language or team owning the domain, very high throughput independent of the frontend, or compliance boundaries. Even then, Nitro remains the BFF in front.
  • The server is a different runtime: no 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).
  • Operations are part of the job: health checks, structured logs with request ids, error monitoring, migrations, zero-downtime deploys, and knowing the cost and limits of the chosen preset.

How the pages fit

PageQuestion it answers
Server routes and h3"Write an endpoint": params, bodies, validation, headers, streaming, middleware, plugins, context
Data layer and databasesPostgres or SQLite, Drizzle or Prisma, migrations, pooling in serverless, N+1, indexes, KV and blobs
Auth and sessions in Nuxtnuxt-auth-utils end to end, OAuth, passkeys, RBAC, SSR forwarding, alternatives
Server caching and performancedefineCachedEventHandler, route rules, storage drivers, invalidation, render cost, cold starts
Files, realtime and background workUploads and presigned URLs, WebSockets and SSE, Nitro tasks, queues, idempotency
API design and integrationREST conventions, OpenAPI, tRPC/oRPC/GraphQL trade-offs, BFF, proxies, webhooks, rate limits
Deployment and operationsPresets, Docker, Cloudflare and Vercel, env and secrets, health, logging, monitoring, migrations, rollbacks

How it works

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
server/api/orders.post.ts
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
})
Gotcha· Business logic in handlers

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.

Exercise

Exercise
  • Sketch the server/ tree for a product you know well; mark which handlers currently contain business logic and which would become services.
  • Build one endpoint the shape above (validate → authorise → service → DTO) and write a unit test for the service and a route test for the handler.

Be able to say

Be able to say· What does fullstack development with Nuxt look like, and when is it the right choice?

"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."

Pages in this section

  1. 01Server routes and h3Everything 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.
  2. 02Data layer and databasesChoosing Postgres, SQLite or a KV, Drizzle versus Prisma versus Kysely, migrations in CI, connection pooling in serverless and edge, transactions and isolation, N+1 and indexes, keyset pagination, optimistic concurrency, DTOs, and Nitro's storage layer for KV and blobs.
  3. 03Auth and sessions in NuxtImplementing authentication end to end with nuxt-auth-utils — sealed sessions, password and OAuth login, passkeys, session hooks — plus authorisation as data with role and object checks, SSR cookie forwarding, multi-tenant context, logout and invalidation, and the alternatives (Better Auth, Auth.js, hosted providers).
  4. 04Server caching and performanceThe server-side cache stack in Nuxt — defineCachedEventHandler and cachedFunction with their options, storage drivers for multi-instance deployments, route rules, CDN headers, invalidation without tags, request coalescing — and the render and query costs that caching cannot fix — sequential awaits, N+1, cold starts, payload size, streaming and observability.
  5. 05Files, realtime and background workUploads through the server or with presigned URLs, validation and processing, serving files safely; WebSockets and server-sent events from the server's point of view and how they scale; Nitro tasks and scheduled tasks, queues, idempotency, retries and the outbox pattern; and post-response work with waitUntil.
  6. 06API design and integrationREST conventions that make an API predictable — resources, methods, status codes, a consistent error shape, pagination, filtering, versioning, idempotency keys, conditional requests — plus OpenAPI from Nitro, when tRPC, oRPC or GraphQL earn their complexity, the backend-for-frontend pattern, proxies, webhooks in and out, API keys and rate limits.
  7. 07Deployment and operationsNitro presets and what each implies — Node in Docker, Cloudflare Workers with bindings, Vercel and Netlify, static — plus runtime configuration and secrets, health checks and graceful shutdown, structured logging with request ids, error and performance monitoring, migrations in the release, zero-downtime deploys and rollbacks, cost, and the NuxtHub shift to self-hosting.

Your notes

Open in Notes

Nothing yet. Notes filed here show up on every page of this section.