️ Cette bibliothèque a été écrite pour prendre en charge les itinéraires API qui utilisent le routeur des pages suivants. Il n'a pas été testé avec le routeur de l'application.
Les routes de l'API suivantes sont un moyen ridiculement amusant et simple d'ajouter des fonctionnalités backend à une application React. Cependant, quand vient le temps d'ajouter du middleware, il n'y a pas de moyen facile de l'implémenter.
Les documents officiels NEXT.JS recommandent d'écrire des fonctions à l'intérieur de votre gestionnaire d'itinéraire API. Il s'agit d'un énorme pas en arrière par rapport aux API Clean fournies par express.js ou koa.js.
Cette bibliothèque tente de fournir des modèles de middleware minimaux, propres et composables qui sont à la fois productifs et agréables à utiliser.
labeluse import { label , Middleware } from "next-api-middleware" ;
import * as Sentry from "@sentry/nextjs" ;
import nanoid from "nanoid" ;
// 1 – Create middleware functions
const captureErrors : Middleware = async ( req , res , next ) => {
try {
// Catch any errors that are thrown in remaining
// middleware and the API route handler
await next ( ) ;
} catch ( err ) {
const eventId = Sentry . captureException ( err ) ;
res . status ( 500 ) ;
res . json ( { error : err } ) ;
}
} ;
const addRequestId : Middleware = async ( req , res , next ) => {
// Let remaining middleware and API route execute
await next ( ) ;
// Apply header
res . setHeader ( "X-Response-ID" , nanoid ( ) ) ;
} ;
// 2 – Use `label` to assemble all middleware
const withMiddleware = label (
{
addRequestId ,
sentry : captureErrors , // <-- Optionally alias middleware
} ,
[ "sentry" ] // <-- Provide a list of middleware to call automatically
) ;
// 3 – Define your API route handler
const apiRouteHandler = async ( req , res ) => {
res . status ( 200 ) ;
res . send ( "Hello world!" ) ;
} ;
// 4 – Choose middleware to invoke for this API route
export default withMiddleware ( "addRequestId" ) ( apiRouteHandler ) ; Mon modèle mental pour la façon dont cette bibliothèque gère les fonctions du middleware est celle d'une "pile d'enroulement et de détention".
Imaginons que vous avez utilisé label pour ajouter deux fonctions middleware à une route API.
Lorsqu'une demande arrive, il s'agit d'une impression approximative de la façon dont cette demande se fraye un chemin à travers toutes les fonctions middleware, le gestionnaire de routes API lui-même, puis recule à travers le middleware.
|-----------------|-----------------|--------------------|
| Middleware #1 | Middleware #2 | API Route Handler |
|-----------------|-----------------|--------------------|
| | | |
Request ------|----> Setup -----|----> Setup -----|-->------| |
| | | | |
|-----------------|-----------------| V |
| | | |
| await next() | await next() | API stuff |
| | | |
|-----------------|-----------------| | |
| | | | |
Response <----|--- Teardown <---|--- Teardown <---|---------| |
| | | |
|-----------------|-----------------|--------------------|
Bien qu'il s'agisse d'un diagramme ASCII minable, je pense que cela donne la bonne impression. La demande serpente à son chemin, bien que chaque fonction de middleware successive, frappe le gestionnaire d'itinéraire API, puis procède à "se détendre" à travers la pile.
Chaque fonction de middleware a la possibilité de passer par trois phases:
La phase "Configuration" couvre tout ce qui se passe avant await next() . La phase "d'attente" est vraiment await next() . La phase "Découragement" est le code restant dans une fonction middleware après await next() .
Il convient de noter que bien que ces phases soient disponibles pour toutes les fonctions de middleware, vous n'avez pas besoin d'en profiter toutes.
Par exemple, dans l'erreur de capture de middleware, vous pourriez simplement envelopper await next() dans un bloc try / catch . D'un autre côté, vous pourriez avoir demandé le middleware de synchronisation qui capture une heure de début pendant la phase de configuration, attend, puis capture un temps d'arrivée dans la phase de démontage.
labelIl s'agit de l'utilité principale pour créer des collections réutilisées de middleware pour une utilisation sur de nombreux routes API suivantes.
const withMiddleware = label ( middleware , defaults ) ; middleware : un objet contenant des fonctions de middleware ou des tableaux de middlewaredefaults : (facultatif) Un tableau de clés middleware qui sera invoqué automatiquement label renvoie une fonction (conventionnellement appelée withMiddleware ) qui utilise le curry pour accepter une liste de noms de middleware à invoquer, suivi d'une fonction de gestionnaire API suivante.
En règle générale, withMiddleware sera importé dans des fichiers d'itinéraire API et utilisé dans l'instruction d'exportation par défaut:
import { withMiddleware } from "../helpers/my-middleware" ;
const apiRouteHandler = async ( req , res ) => {
...
}
export default withMiddleware ( "foo" , "bar" , "baz" ) ( apiRouteHandler ) ; Bien que label puisse contenir de nombreuses fonctions de middleware, le middleware réel invoqué par une voie d'API est déterminé par les noms transmis à withMiddleware .
const logErrors = async ( req , res , next ) => {
try {
await next ( ) ;
} catch ( error ) {
console . error ( error ) ;
res . status ( 500 ) ;
res . json ( { error } ) ;
}
} ;
const withMiddleware = label ( {
logErrors ,
} ) ;
// export default withMiddleware("logErrors")(apiRouteHandler); const withMiddleware = label ( {
error : logErrors ,
} ) ;
// export default withMiddleware("error")(apiRouteHandler); import { foo , bar , baz } from "./my-middleware" ;
const withMiddleware = label ( {
error : logErrors ,
myGroup : [ foo , bar , baz ] ,
} ) ;
// export default withMiddleware("error", "myGroup")(apiRouteHandler); const withMiddleware = label (
{
error : logErrors ,
myGroup : [ foo , bar , baz ] ,
} ,
[ "error" ]
) ;
// export default withMiddleware("myGroup")(apiRouteHandler);use Cet utilitaire accepte directement les fonctions middleware et les exécute tous dans l'ordre. Il s'agit d'une alternative plus simple à label qui peut être utile pour gérer les fonctions de middleware unique.
const withInlineMiddleware = use ( ... middleware ) ; middleware : une liste des fonctions middleware et / ou des tableaux de fonctions middleware use Renvoie une fonction qui accepte un gestionnaire d'itinéraire API Next.js.
import { use } from "next-api-middleware" ;
import cors from "cors" ;
const apiRouteThatOnlyNeedsCORS = async ( req , res ) => {
...
}
export default use ( cors ( ) ) ( apiRouteThatOnlyNeedsCORS ) ; Voir des exemples.md pour des exemples plus détaillés d' label et use .
Étant donné que use et label acceptent les valeurs qui évaluent les fonctions de middleware, cela offre la possibilité de créer des usines de middleware personnalisées.
Voici un exemple d'une usine qui génère une fonction middleware pour n'autoriser que les demandes avec une méthode HTTP donnée:
import { Middleware } from "next-api-middleware" ;
const httpMethod = (
allowedHttpMethod : "GET" | "POST" | "PATCH"
) : Middleware => {
return async function ( req , res , next ) {
if ( req . method === allowedHttpMethod || req . method == "OPTIONS" ) {
await next ( ) ;
} else {
res . status ( 404 ) ;
res . end ( ) ;
}
} ;
} ;
export const postRequestsOnlyMiddleware = httpMethod ( "POST" ) ; Middleware est inspiré par le style middleware asyncronique popularisé par Koa.js.
type Middleware < Request = NextApiRequest , Response = NextApiResponse > = (
req : Request ,
res : Response ,
next : ( ) => Promise < void >
) => Promise < void > ;