Impara a creare potenti endpoint API con i Route Handler di Next.js. Questa guida copre tutto, dalla configurazione base alle tecniche avanzate, con esempi pratici e best practice.
Route Handler di Next.js: Una Guida Completa alla Creazione di Endpoint API
Next.js ha rivoluzionato il modo in cui costruiamo applicazioni web con le sue potenti funzionalità come il rendering lato server, la generazione di siti statici e, ora, i Route Handler. I Route Handler forniscono un modo flessibile ed efficiente per creare endpoint API direttamente all'interno della tua applicazione Next.js. Questa guida esplora il concetto di Route Handler, i loro vantaggi e come utilizzarli efficacemente per costruire API robuste.
Cosa sono i Route Handler di Next.js?
I Route Handler sono funzioni definite all'interno della directory app
di un progetto Next.js che gestiscono le richieste HTTP in arrivo. A differenza del vecchio approccio pages/api
(che utilizza le API Routes), i Route Handler offrono un modo più snello e flessibile per definire gli endpoint API accanto ai tuoi componenti React. Sono essenzialmente funzioni serverless eseguite sull'edge o nell'ambiente server da te scelto.
Pensa ai Route Handler come alla logica di backend della tua applicazione Next.js, responsabile dell'elaborazione delle richieste, dell'interazione con i database e della restituzione delle risposte.
Vantaggi dell'uso dei Route Handler
- Colocazione: I Route Handler risiedono direttamente accanto ai tuoi componenti React all'interno della directory
app
, promuovendo una migliore organizzazione e manutenibilità del codice. - Supporto TypeScript: Il supporto TypeScript integrato garantisce la sicurezza dei tipi e migliora l'esperienza dello sviluppatore.
- Integrazione Middleware: Integra facilmente middleware per compiti come autenticazione, autorizzazione e validazione delle richieste.
- Supporto Streaming: I Route Handler possono trasmettere dati in streaming, consentendoti di inviare risposte in modo incrementale, il che è vantaggioso per grandi set di dati o processi di lunga durata.
- Edge Functions: Esegui il deployment dei Route Handler come Edge Functions per risposte a bassa latenza più vicine ai tuoi utenti, sfruttando le CDN globali.
- Design API Semplificato: I Route Handler forniscono un'API pulita e intuitiva per la gestione di richieste e risposte.
- Integrazione con Server Actions: La stretta integrazione con le Server Actions consente una comunicazione fluida tra i tuoi componenti lato client e la logica lato server.
Configurare il tuo progetto Next.js
Prima di immergerti nei Route Handler, assicurati di avere un progetto Next.js configurato con la directory app
. Se stai iniziando un nuovo progetto, usa il seguente comando:
npx create-next-app@latest my-nextjs-app
Scegli la directory app
durante il processo di configurazione per abilitare il nuovo sistema di routing.
Creare il tuo primo Route Handler
Creiamo un semplice endpoint API che restituisce una risposta JSON. Crea una nuova directory all'interno della directory app
, ad esempio, /app/api/hello
. All'interno di questa directory, crea un file chiamato route.ts
(o route.js
se non stai usando TypeScript).
Ecco il codice per il tuo primo Route Handler:
// app/api/hello/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Ciao dai Route Handler di Next.js!' });
}
Spiegazione:
import { NextResponse } from 'next/server';
: Importa l'oggettoNextResponse
, che viene utilizzato per costruire le risposte API.export async function GET(request: Request) { ... }
: Definisce una funzione asincrona che gestisce le richieste GET all'endpoint/api/hello
. Il parametrorequest
fornisce accesso all'oggetto della richiesta in arrivo.return NextResponse.json({ message: 'Ciao dai Route Handler di Next.js!' });
: Crea una risposta JSON con un messaggio e la restituisce usandoNextResponse.json()
.
Ora, puoi accedere a questo endpoint navigando su /api/hello
nel tuo browser o usando uno strumento come curl
o Postman
.
Gestire i diversi metodi HTTP
I Route Handler supportano vari metodi HTTP come GET, POST, PUT, DELETE, PATCH e OPTIONS. Puoi definire funzioni separate per ogni metodo all'interno dello stesso file route.ts
.
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Logica per recuperare tutti gli utenti dal database
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // Dati di esempio
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json(); // Esegue il parsing del corpo della richiesta come JSON
// Logica per creare un nuovo utente nel database usando 'data'
const newUser = { id: 3, name: data.name, email: data.email }; // Esempio
return NextResponse.json(newUser, { status: 201 }); // Restituisce il nuovo utente con un codice di stato 201 Created
}
Spiegazione:
- La funzione
GET
recupera un elenco di utenti (simulato qui) e li restituisce come risposta JSON. - La funzione
POST
esegue il parsing del corpo della richiesta come JSON, crea un nuovo utente (simulato) e restituisce il nuovo utente con un codice di stato 201 Created.
Accedere ai dati della richiesta
L'oggetto request
fornisce accesso a varie informazioni sulla richiesta in arrivo, inclusi header, parametri di query e il corpo della richiesta.
Header
Puoi accedere agli header della richiesta usando la proprietà request.headers
:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent');
console.log('User Agent:', userAgent);
return NextResponse.json({ userAgent });
}
Parametri di query
Per accedere ai parametri di query, puoi usare il costruttore URL
:
export async function GET(request: Request) {
const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const id = searchParams.get('id');
console.log('ID:', id);
return NextResponse.json({ id });
}
Corpo della richiesta
Per le richieste POST, PUT e PATCH, puoi accedere al corpo della richiesta usando i metodi request.json()
o request.text()
, a seconda del tipo di contenuto.
export async function POST(request: Request) {
const data = await request.json();
console.log('Data:', data);
return NextResponse.json({ receivedData: data });
}
Restituire le risposte
L'oggetto NextResponse
è usato per costruire le risposte API. Fornisce diversi metodi per impostare header, codici di stato e corpi delle risposte.
Risposte JSON
Usa il metodo NextResponse.json()
per restituire risposte JSON:
return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });
Risposte testuali
Usa il costruttore new Response()
per restituire risposte di testo semplice:
return new Response('Ciao, mondo!', { status: 200, headers: { 'Content-Type': 'text/plain' } });
Reindirizzamenti
Usa NextResponse.redirect()
per reindirizzare gli utenti a un URL diverso:
import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.redirect(new URL('/new-location', request.url));
}
Impostare gli header
Puoi impostare header personalizzati usando l'opzione headers
in NextResponse.json()
o new Response()
:
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });
Integrazione dei Middleware
Il middleware ti permette di eseguire codice prima che una richiesta venga gestita dal tuo Route Handler. Questo è utile per l'autenticazione, l'autorizzazione, il logging e altre problematiche trasversali.
Per creare un middleware, crea un file chiamato middleware.ts
(o middleware.js
) nella directory app
o in qualsiasi sottodirectory. Il middleware si applicherà a tutte le route all'interno di quella directory e delle sue sottodirectory.
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'], // Applica questo middleware ai percorsi che iniziano con /protected/
};
Spiegazione:
- La funzione
middleware
controlla la presenza di un token di autenticazione nei cookie della richiesta. - Se il token è mancante, reindirizza l'utente alla pagina di login.
- Altrimenti, consente alla richiesta di procedere verso il Route Handler.
- L'oggetto
config
specifica che questo middleware deve essere applicato solo alle route che iniziano con/protected/
.
Gestione degli errori
Una corretta gestione degli errori è cruciale per costruire API robuste. Puoi usare i blocchi try...catch
per gestire le eccezioni e restituire risposte di errore appropriate.
export async function GET(request: Request) {
try {
// Simula un errore
throw new Error('Qualcosa è andato storto!');
} catch (error: any) {
console.error('Errore:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Spiegazione:
- Il blocco
try...catch
cattura qualsiasi eccezione che si verifica all'interno del Route Handler. - Nel blocco
catch
, l'errore viene registrato e viene restituita una risposta di errore con un codice di stato 500 Internal Server Error.
Risposte in streaming
I Route Handler supportano le risposte in streaming, che ti permettono di inviare dati in modo incrementale al client. Questo è particolarmente utile per grandi set di dati o processi di lunga durata.
import { Readable } from 'stream';
import { NextResponse } from 'next/server';
async function* generateData() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simula un ritardo
yield `Blocco di dati ${i}\n`;
}
}
export async function GET(request: Request) {
const readableStream = Readable.from(generateData());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
Spiegazione:
- La funzione
generateData
è un generatore asincrono che produce blocchi di dati con un ritardo. - Il metodo
Readable.from()
crea uno stream leggibile dal generatore. - L'oggetto
Response
viene creato con lo stream leggibile come corpo, e l'headerContent-Type
è impostato sutext/plain
.
Autenticazione e Autorizzazione
Mettere in sicurezza i tuoi endpoint API è cruciale. Puoi implementare l'autenticazione e l'autorizzazione usando middleware o direttamente all'interno dei tuoi Route Handler.
Autenticazione
L'autenticazione verifica l'identità dell'utente che effettua la richiesta. I metodi di autenticazione comuni includono:
- JWT (JSON Web Tokens): Genera un token al login riuscito e verificalo nelle richieste successive.
- Autenticazione basata su sessione: Usa i cookie per memorizzare gli identificatori di sessione e verificarli ad ogni richiesta.
- OAuth: Delega l'autenticazione a un provider di terze parti come Google o Facebook.
Ecco un esempio di autenticazione JWT tramite middleware:
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';
const secret = process.env.JWT_SECRET || 'la-tua-chiave-segreta'; // Sostituisci con un segreto forte e generato casualmente
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.json({ message: 'Autenticazione richiesta' }, { status: 401 });
}
try {
jwt.verify(token, secret);
return NextResponse.next();
} catch (error) {
return NextResponse.json({ message: 'Token non valido' }, { status: 401 });
}
}
export const config = {
matcher: ['/api/protected/:path*'],
};
Autorizzazione
L'autorizzazione determina a quali risorse un utente è autorizzato ad accedere. Questo si basa tipicamente su ruoli o permessi.
Puoi implementare l'autorizzazione all'interno dei tuoi Route Handler controllando i ruoli o i permessi dell'utente e restituendo un errore se non hanno accesso.
// app/api/admin/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Assumiamo che tu abbia una funzione per ottenere il ruolo dell'utente dal token o dalla sessione
const userRole = await getUserRole(request);
if (userRole !== 'admin') {
return NextResponse.json({ message: 'Non autorizzato' }, { status: 403 });
}
// Logica per recuperare i dati dell'amministratore
const adminData = { message: 'Dati amministratore' };
return NextResponse.json(adminData);
}
async function getUserRole(request: Request): Promise {
// Sostituisci con la tua logica effettiva per estrarre il ruolo dell'utente dalla richiesta
// Questo potrebbe comportare la verifica di un token JWT o il controllo di una sessione
return 'admin'; // Esempio: ruolo hardcoded per dimostrazione
}
Deployment dei Route Handler
I Route Handler vengono distribuiti come funzioni serverless sul tuo provider di hosting prescelto. Next.js supporta varie piattaforme di deployment, tra cui Vercel, Netlify, AWS e altre.
Per Vercel, il deployment è semplice come collegare il tuo repository Git a Vercel e inviare il tuo codice. Vercel rileva automaticamente il tuo progetto Next.js e distribuisce i tuoi Route Handler come funzioni serverless.
Tecniche Avanzate
Edge Functions
I Route Handler possono essere distribuiti come Edge Functions, che vengono eseguite sull'edge di una CDN, più vicino ai tuoi utenti. Questo può ridurre significativamente la latenza e migliorare le prestazioni.
Per distribuire un Route Handler come Edge Function, aggiungi il runtime edge
al tuo file route.ts
:
export const runtime = 'edge';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Ciao dall\'Edge!' });
}
Server Actions
Le Server Actions ti permettono di eseguire codice lato server direttamente dai tuoi componenti React. I Route Handler e le Server Actions funzionano perfettamente insieme, permettendoti di costruire applicazioni complesse con facilità.
Ecco un esempio di utilizzo di una Server Action per chiamare un Route Handler:
// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
async function handleSubmit(data: FormData) {
'use server';
const name = data.get('name');
const email = data.get('email');
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name, email }),
});
if (response.ok) {
router.refresh(); // Aggiorna la pagina per riflettere le modifiche
}
}
export default function MyComponent() {
const router = useRouter();
return (
);
}
Caching
Il caching può migliorare significativamente le prestazioni dei tuoi endpoint API. Puoi usare l'header Cache-Control
per controllare come le tue risposte vengono memorizzate nella cache dai browser e dalle CDN.
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });
Questo esempio imposta l'header Cache-Control
su public, max-age=3600
, che indica ai browser e alle CDN di memorizzare la risposta nella cache per un'ora.
Best Practice
- Usa TypeScript: Sfrutta la sicurezza dei tipi di TypeScript per migliorare la qualità del codice e prevenire errori.
- Valida le Richieste: Valida le richieste in arrivo per garantire l'integrità dei dati e prevenire input malevoli.
- Gestisci gli Errori con Eleganza: Implementa una corretta gestione degli errori per fornire messaggi di errore informativi ai client.
- Metti in Sicurezza i Tuoi Endpoint: Implementa l'autenticazione e l'autorizzazione per proteggere i tuoi endpoint API.
- Usa i Middleware: Usa i middleware per problematiche trasversali come l'autenticazione, il logging e la validazione delle richieste.
- Metti in Cache le Risposte: Usa il caching per migliorare le prestazioni dei tuoi endpoint API.
- Monitora le Tue API: Monitora le tue API per identificare e risolvere rapidamente i problemi.
- Documenta le Tue API: Documenta le tue API per renderle facili da usare per altri sviluppatori. Considera l'uso di strumenti come Swagger/OpenAPI per la documentazione delle API.
Esempi dal mondo reale
Ecco alcuni esempi reali di come possono essere usati i Route Handler:
- API E-commerce: Crea endpoint API per la gestione di prodotti, ordini e utenti.
- API Social Media: Crea endpoint API per pubblicare tweet, seguire utenti e recuperare timeline.
- API di un Content Management System (CMS): Crea endpoint API per la gestione di contenuti, utenti e impostazioni.
- API di Analisi Dati: Crea endpoint API per la raccolta e l'analisi di dati. Ad esempio, un Route Handler potrebbe ricevere dati da pixel di tracciamento su diversi siti web e aggregare le informazioni per il reporting.
Esempio di E-commerce internazionale: Un Route Handler utilizzato per recuperare i prezzi dei prodotti in base al paese dell'utente. L'endpoint potrebbe usare la geolocalizzazione della richiesta (derivata dall'indirizzo IP) per determinare la posizione dell'utente e restituire i prezzi nella valuta appropriata. Questo contribuisce a un'esperienza di acquisto localizzata.
Esempio di Autenticazione globale: Un Route Handler che implementa l'autenticazione a più fattori (MFA) per utenti in tutto il mondo. Questo potrebbe comportare l'invio di codici SMS o l'uso di app di autenticazione, nel rispetto delle normative sulla privacy e delle infrastrutture di telecomunicazione delle diverse regioni.
Distribuzione di contenuti multilingue: Un Route Handler che fornisce contenuti nella lingua preferita dell'utente. Questa può essere determinata dall'header `Accept-Language` nella richiesta. Questo esempio evidenzia la necessità di una corretta codifica UTF-8 e del supporto per le lingue da destra a sinistra, se appropriato.
Conclusione
I Route Handler di Next.js forniscono un modo potente e flessibile per creare endpoint API direttamente all'interno della tua applicazione Next.js. Sfruttando i Route Handler, puoi costruire API robuste con facilità, collocare la logica di backend insieme ai tuoi componenti React e approfittare di funzionalità come middleware, streaming e Edge Functions.
Questa guida completa ha coperto tutto, dalla configurazione di base alle tecniche avanzate. Seguendo le best practice delineate in questa guida, puoi costruire API di alta qualità che siano sicure, performanti e manutenibili.