Nuxt Nitro e Server Routes nel 2026: Vue Full-Stack ed Endpoint API
Guida completa alle Nuxt Nitro Server Routes: creazione di endpoint API, implementazione middleware, integrazione database e strategie di deployment per applicazioni Vue full-stack.

Nuxt Nitro trasforma le applicazioni Vue in potenti soluzioni full-stack fornendo un motore server universale che gestisce route API, middleware e logica lato server insieme ai componenti frontend. Con Nuxt 4.3 e il prossimo Nitro v3, lo sviluppo di backend pronti per la produzione non è mai stato così integrato con lo sviluppo Vue.
Nitro permette la condivisione di tipi TypeScript tra frontend e backend, eliminando la deriva dei contratti API e riducendo il boilerplate. Le server routes risiedono in server/api/ e diventano automaticamente disponibili agli endpoint /api/*.
Comprendere l'Architettura del Motore Server Nuxt Nitro
Nitro funge da runtime server JavaScript universale che alimenta la directory server di Nuxt. A differenza dei tradizionali server Node.js, Nitro compila in bundle ottimizzati per piattaforma che si deployano senza modifiche al codice su Vercel, Netlify, Cloudflare Workers e ambienti Node.js standard.
L'architettura separa le responsabilità attraverso convenzioni di directory:
export default defineEventHandler(async (event) => {
// Nitro serializza automaticamente i valori di ritorno in JSON
// Il suffisso del metodo HTTP (.get.ts) limita alle richieste GET
const users = await getUsersFromDatabase()
return { users, count: users.length }
})Nitro analizza quattro directory: server/api per le route API con prefisso /api, server/routes per le route senza prefisso, server/middleware per gli interceptor delle richieste e server/utils per le utility server condivise. I file al di fuori di queste directory rimangono invisibili al runtime del server.
Creare Endpoint API RESTful con defineEventHandler
Le server routes seguono convenzioni di routing basate su file dove i nomi dei file mappano direttamente ai percorsi URL. I suffissi dei metodi HTTP controllano quali richieste ogni handler accetta.
// Gestisce GET /api/products/123
export default defineEventHandler(async (event) => {
// Estrarre il parametro route dall'URL
const id = getRouterParam(event, 'id')
if (!id) {
throw createError({
status: 400, // Nuxt 4 usa la nomenclatura Web API (non statusCode)
statusText: 'Product ID required' // Non statusMessage
})
}
const product = await fetchProduct(id)
if (!product) {
throw createError({
status: 404,
statusText: 'Product not found'
})
}
return product
})Nuxt 4 adotta le convenzioni di denominazione Web API in preparazione per Nitro v3. La proprietà statusCode diventa status, e statusMessage diventa statusText. Mentre le proprietà legacy continuano a funzionare, la migrazione alla nuova nomenclatura garantisce compatibilità futura con Nuxt 5.
Gestire le richieste POST con validazione del body:
// Gestisce POST /api/products
import { z } from 'zod'
const ProductSchema = z.object({
name: z.string().min(1).max(200),
price: z.number().positive(),
category: z.enum(['electronics', 'clothing', 'food'])
})
export default defineEventHandler(async (event) => {
// readBody analizza automaticamente il JSON
const body = await readBody(event)
// Validare con schema Zod
const result = ProductSchema.safeParse(body)
if (!result.success) {
throw createError({
status: 422,
statusText: 'Validation failed',
data: result.error.flatten()
})
}
const product = await createProduct(result.data)
// Impostare status 201 Created per creazione risorse
setResponseStatus(event, 201)
return product
})Server Middleware per Autenticazione e Logging
I file middleware vengono eseguiti prima degli handler delle route, abilitando funzionalità trasversali come autenticazione, logging e trasformazione delle richieste. A differenza degli handler delle route, i middleware non restituiscono risposte direttamente.
export default defineEventHandler(async (event) => {
// Saltare auth per route pubbliche
const publicRoutes = ['/api/health', '/api/auth/login']
if (publicRoutes.includes(event.path)) {
return // Continuare all'handler successivo
}
const authHeader = getHeader(event, 'authorization')
if (!authHeader?.startsWith('Bearer ')) {
throw createError({
status: 401,
statusText: 'Authentication required'
})
}
const token = authHeader.slice(7)
try {
// Verificare JWT e allegare utente al context dell'evento
const user = await verifyToken(token)
event.context.user = user
} catch {
throw createError({
status: 401,
statusText: 'Invalid or expired token'
})
}
})L'oggetto event.context persiste attraverso middleware e handler all'interno di una singola richiesta, fornendo un modo type-safe per condividere dati. I tipi di context si definiscono in un file di dichiarazione:
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
email: string
role: 'admin' | 'user'
}
}
}Per la preparazione ai colloqui sui concetti server di Nuxt, consultare il modulo Domande di colloquio Nuxt Server Routes.
Pronto a superare i tuoi colloqui su Vue.js / Nuxt.js?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Integrazione Database con Prisma e Server Utils
Le utility server in server/utils/ vengono auto-importate in tutto il codice server, rendendo accessibili i client database e la logica condivisa senza import espliciti.
import { PrismaClient } from '@prisma/client'
// Pattern Singleton previene multiple istanze in sviluppo
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
log: process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error']
})
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}Con il client database disponibile globalmente, gli handler delle route vi accedono direttamente:
export default defineEventHandler(async (event) => {
// Query parameters per paginazione
const query = getQuery(event)
const page = Number(query.page) || 1
const limit = Math.min(Number(query.limit) || 10, 100)
const skip = (page - 1) * limit
// Query parallele per dati e conteggio
const [posts, total] = await Promise.all([
prisma.post.findMany({
skip,
take: limit,
orderBy: { createdAt: 'desc' },
include: { author: { select: { name: true, avatar: true } } }
}),
prisma.post.count()
])
return {
posts,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
}
})Server Plugin per l'Inizializzazione a Runtime
I server plugin in server/plugins/ vengono eseguiti una volta all'avvio del server, ideali per inizializzare connessioni, pianificare task o configurare il monitoring.
export default defineNitroPlugin(async (nitroApp) => {
// Verificare la connessione al database all'avvio
try {
await prisma.$connect()
console.log('Database connected successfully')
} catch (error) {
console.error('Database connection failed:', error)
process.exit(1) // Fallire rapidamente se DB non disponibile
}
// Hook per graceful shutdown
nitroApp.hooks.hook('close', async () => {
await prisma.$disconnect()
console.log('Database disconnected')
})
})Il sistema di plugin si integra con gli hook Nitro per la gestione del lifecycle. Gli hook si attivano in punti specifici: request prima della gestione, beforeResponse prima dell'invio e close durante lo shutdown.
Chiamate API Type-Safe dai Componenti Vue
L'utility $fetch di Nuxt fornisce inferenza di tipo dalle server routes quando si usa l'alias ~/server. Combinato con il composable useFetch, i componenti consumano API con supporto TypeScript completo.
<script setup lang="ts">
// Tipo di ritorno inferito da server/api/products/[id].get.ts
const route = useRoute()
const { data: product, status, error } = await useFetch(
`/api/products/${route.params.id}`
)
// Mutazione con aggiornamenti ottimistici
const { execute: updateProduct, status: updateStatus } = useFetch(
`/api/products/${route.params.id}`,
{
method: 'PATCH',
immediate: false, // Non recuperare al mount
watch: false // Non ri-recuperare al cambio di dipendenza
}
)
async function handleUpdate(updates: Partial<Product>) {
await updateProduct({ body: updates })
// Ri-recuperare per sincronizzare lo state
await refreshNuxtData(`product-${route.params.id}`)
}
</script>
<template>
<div v-if="status === 'pending'">Loading...</div>
<div v-else-if="error">{{ error.message }}</div>
<ProductDetail v-else :product="product" @update="handleUpdate" />
</template>Strategie di Caching per le Server Routes
Nuxt 4.3 introduce controlli di caching avanzati attraverso routeRules in nuxt.config.ts. Le server routes beneficiano della stessa infrastruttura di caching delle pagine.
export default defineNuxtConfig({
routeRules: {
// Cache liste prodotti per 5 minuti
'/api/products': {
cache: {
maxAge: 300,
staleMaxAge: 600, // Servire stale durante la rivalidazione
swr: true
}
},
// Nessun cache per dati specifici dell'utente
'/api/user/**': { cache: false },
// Pre-renderizzare risposte API statiche al build time
'/api/categories': { prerender: true }
}
})Per l'invalidazione dinamica della cache, si usa il wrapper defineCachedEventHandler:
export default defineCachedEventHandler(async (event) => {
const trending = await calculateTrendingProducts()
return trending
}, {
maxAge: 60 * 5, // 5 minuti
name: 'trending-products',
getKey: () => 'trending', // Chiave cache per invalidazione
shouldBypassCache: (event) => {
// Bypassare per utenti admin
return event.context.user?.role === 'admin'
}
})Gestione Errori e Formattazione Response
Risposte di errore consistenti migliorano l'usabilità dell'API. Creare una utility condivisa per la gestione degli errori:
import { H3Error } from 'h3'
export function handleDatabaseError(error: unknown): never {
console.error('Database error:', error)
// Gestione errori specifici di Prisma
if (error instanceof Error && 'code' in error) {
const prismaError = error as { code: string }
if (prismaError.code === 'P2002') {
throw createError({
status: 409,
statusText: 'Resource already exists'
})
}
if (prismaError.code === 'P2025') {
throw createError({
status: 404,
statusText: 'Resource not found'
})
}
}
throw createError({
status: 500,
statusText: 'Internal server error'
})
}Per pattern Vue.js completi e gestione dello state, consultare l'articolo Confronto Pinia vs Vuex.
Prima di deployare server Nitro: validare le variabili d'ambiente con schemi Zod, implementare middleware di rate limiting, aggiungere logging delle richieste per il debugging e configurare header CORS per l'accesso API cross-origin.
Deployment su Diverse Piattaforme
Il sistema di preset di Nitro genera build ottimizzati per ogni piattaforma:
export default defineNuxtConfig({
nitro: {
// Rilevato automaticamente su Vercel/Netlify, o specificare manualmente
preset: 'node-server', // 'vercel', 'netlify', 'cloudflare-workers'
// Comprimere le risposte
compressPublicAssets: true,
// Pacchetti esterni da non bundlare
externals: {
external: ['@prisma/client']
}
}
})Per i deployment Node.js, l'output include un server standalone:
# Build per produzione
npm run build
# Avviare il server
node .output/server/index.mjsNuxt 3 raggiunge l'end-of-life il 31 luglio 2026. I progetti dovrebbero migrare a Nuxt 4 usando la guida ufficiale alla migrazione. Nuxt 5 con Nitro v3 seguirà poco dopo.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Conclusione
- Le server routes in
server/api/mappano automaticamente agli endpoint/api/*con suffissi dei metodi HTTP (.get.ts,.post.ts) che controllano i tipi di richiesta - I middleware in
server/middleware/gestiscono autenticazione, logging e trasformazione delle richieste prima dell'esecuzione delle route - L'oggetto
event.contextcondivide dati tipizzati tra middleware e handler all'interno di una richiesta - Le server utils in
server/utils/vengono auto-importate nel codice server, ideali per client database e logica condivisa - Nuxt 4 adotta la nomenclatura Web API (
status/statusText) in preparazione per Nitro v3 defineCachedEventHandlerfornisce caching a livello di route con controlli di invalidazione- I preset Nitro abilitano deployment zero-config su Vercel, Netlify, Cloudflare e server Node.js
Condividi
Articoli correlati

Vue 3 con TypeScript nel 2026: props, emits e composable type-safe
Padroneggiare componenti Vue 3 type-safe con TypeScript: defineProps generico, defineEmits a tupla, composable tipizzati, defineModel e InjectionKey, con domande da colloquio.

Testing in Vue 3 nel 2026: Vitest, Vue Test Utils e domande da colloquio
Guida pratica al testing in Vue nel 2026: configurazione di Vitest, montaggio dei componenti con Vue Test Utils, test di composable e store Pinia, mock delle API, misurazione della copertura e le domande da colloquio poste dai team di selezione.

Performance di Vue 3 nel 2026: Vapor Mode, Alien Signals e la fine del Virtual DOM
Analisi approfondita delle prestazioni di Vue 3.6 Vapor Mode: come elimina il Virtual DOM, il sistema di reattività Alien Signals, benchmark contro Solid.js e tecniche di ottimizzazione pratiche per app in produzione.