O roteamento de métodos baseado em promessa e a camada de middleware para as rotas da API Next.js, rotas de API de borda, middleware, roteador de aplicativos Next.js e GetServersideProps.
npm install next-connect@nextConfira também a pasta Exemplos.
next-connect pode ser usado em rotas de API.
// pages/api/user/[id].ts
import type { NextApiRequest , NextApiResponse } from "next" ;
import { createRouter , expressWrapper } from "next-connect" ;
import cors from "cors" ;
const router = createRouter < NextApiRequest , NextApiResponse > ( ) ;
router
// Use express middleware in next-connect with expressWrapper function
. use ( expressWrapper ( passport . session ( ) ) )
// A middleware example
. use ( async ( req , res , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req , res ) => {
const user = getUser ( req . query . id ) ;
res . json ( { user } ) ;
} )
. put ( ( req , res ) => {
if ( req . user . id !== req . query . id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
res . json ( { user } ) ;
} ) ;
export const config = {
runtime : "edge" ,
} ;
export default router . handler ( {
onError : ( err , req , res ) => {
console . error ( err . stack ) ;
res . status ( err . statusCode || 500 ) . end ( err . message ) ;
} ,
} ) ; next-connect pode ser usada em rotas de API de borda
// pages/api/user/[id].ts
import type { NextFetchEvent , NextRequest } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
import cors from "cors" ;
const router = createEdgeRouter < NextRequest , NextFetchEvent > ( ) ;
router
// A middleware example
. use ( async ( req , event , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req ) => {
const id = req . nextUrl . searchParams . get ( "id" ) ;
const user = getUser ( id ) ;
return NextResponse . json ( { user } ) ;
} )
. put ( ( req ) => {
const id = req . nextUrl . searchParams . get ( "id" ) ;
if ( req . user . id !== id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
return NextResponse . json ( { user } ) ;
} ) ;
export default router . handler ( {
onError : ( err , req , event ) => {
console . error ( err . stack ) ;
return new NextResponse ( "Something broke!" , {
status : err . statusCode || 500 ,
} ) ;
} ,
} ) ; next-connect pode ser usada no próximo.js 13 manipulador de rota. A maneira como os manipuladores são escritos é quase o mesmo para o próximo.js API Rotas de borda usando createEdgeRouter .
// app/api/user/[id]/route.ts
import type { NextFetchEvent , NextRequest } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
import cors from "cors" ;
interface RequestContext {
params : {
id : string ;
} ;
}
const router = createEdgeRouter < NextRequest , RequestContext > ( ) ;
router
// A middleware example
. use ( async ( req , event , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req ) => {
const id = req . params . id ;
const user = getUser ( id ) ;
return NextResponse . json ( { user } ) ;
} )
. put ( ( req ) => {
const id = req . params . id ;
if ( req . user . id !== id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
return NextResponse . json ( { user } ) ;
} ) ;
export async function GET ( request : NextRequest , ctx : RequestContext ) {
return router . run ( request , ctx ) ;
}
export async function PUT ( request : NextRequest , ctx : RequestContext ) {
return router . run ( request , ctx ) ;
} next-connect pode ser usada no middleware do próximo.js
// middleware.ts
import { NextResponse } from "next/server" ;
import type { NextRequest , NextFetchEvent } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
const router = createEdgeRouter < NextRequest , NextFetchEvent > ( ) ;
router . use ( async ( request , event , next ) => {
// logging request example
console . log ( ` ${ request . method } ${ request . url } ` ) ;
return next ( ) ;
} ) ;
router . get ( "/about" , ( request ) => {
return NextResponse . redirect ( new URL ( "/about-2" , request . url ) ) ;
} ) ;
router . use ( "/dashboard" , ( request ) => {
if ( ! isAuthenticated ( request ) ) {
return NextResponse . redirect ( new URL ( "/login" , request . url ) ) ;
}
return NextResponse . next ( ) ;
} ) ;
router . all ( ( ) => {
// default if none of the above matches
return NextResponse . next ( ) ;
} ) ;
export function middleware ( request : NextRequest , event : NextFetchEvent ) {
return router . run ( request , event ) ;
}
export const config = {
matcher : [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!api|_next/static|_next/image|favicon.ico).*)" ,
] ,
} ; next-connect pode ser usado no GetServersideProps.
// pages/users/[id].js
import { createRouter } from "next-connect" ;
export default function Page ( { user , updated } ) {
return (
< div >
{ updated && < p > User has been updated </ p > }
< div > { JSON . stringify ( user ) } </ div >
< form method = "POST" > { /* User update form */ } </ form >
</ div >
) ;
}
const router = createRouter ( )
. use ( async ( req , res , next ) => {
// this serve as the error handling middleware
try {
return await next ( ) ;
} catch ( e ) {
return {
props : { error : e . message } ,
} ;
}
} )
. get ( async ( req , res ) => {
const user = await getUser ( req . params . id ) ;
if ( ! user ) {
// https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#notfound
return { props : { notFound : true } } ;
}
return { props : { user } } ;
} )
. put ( async ( req , res ) => {
const user = await updateUser ( req ) ;
return { props : { user , updated : true } } ;
} ) ;
export async function getServerSideProps ( { req , res } ) {
return router . run ( req , res ) ;
} As seguintes APIs são reescritas em termo de NodeRouter ( createRouter ), mas também se aplicam ao EdgeRouter ( createEdgeRouter ).
Crie um roteador Node.js de instância.
base (Opcional) - Combine todas as rotas à direita da base ou corresponda a tudo se omitido. (Nota: se usado no próximo.js, isso é frequentemente omitido)
fn (s) pode ser:
(req, res[, next]) // Mount a middleware function
router1 . use ( async ( req , res , next ) => {
req . hello = "world" ;
await next ( ) ; // call to proceed to the next in chain
console . log ( "request is done" ) ; // call after all downstream handler has run
} ) ;
// Or include a base
router2 . use ( "/foo" , fn ) ; // Only run in /foo/**
// mount an instance of router
const sub1 = createRouter ( ) . use ( fn1 , fn2 ) ;
const sub2 = createRouter ( ) . use ( "/dashboard" , auth ) ;
const sub3 = createRouter ( )
. use ( "/waldo" , subby )
. get ( getty )
. post ( "/baz" , posty )
. put ( "/" , putty ) ;
router3
// - fn1 and fn2 always run
// - auth runs only on /dashboard
. use ( sub1 , sub2 )
// `subby` runs on ANY /foo/waldo?/*
// `getty` runs on GET /foo/*
// `posty` runs on POST /foo/baz
// `putty` runs on PUT /foo
. use ( "/foo" , sub3 ) ; METHOD é um método HTTP ( GET , HEAD , POST , PUT , PATCH , DELETE , OPTIONS , TRACE ) em minúsculas.
pattern (opcional) - Combine rotas com base no padrão suportado ou correspondam a qualquer um se omitido.
fn (s) são funções de (req, res[, next]) .
router . get ( "/api/user" , ( req , res , next ) => {
res . json ( req . user ) ;
} ) ;
router . post ( "/api/users" , ( req , res , next ) => {
res . end ( "User created" ) ;
} ) ;
router . put ( "/api/user/:id" , ( req , res , next ) => {
// https://nextjs.org/docs/routing/dynamic-routes
res . end ( `User ${ req . params . id } updated` ) ;
} ) ;
// Next.js already handles routing (including dynamic routes), we often
// omit `pattern` in `.METHOD`
router . get ( ( req , res , next ) => {
res . end ( "This matches whatever route" ) ;
} ) ;NOTA você deve entender o roteamento baseado no sistema de arquivos do próximo.js. Por exemplo, ter um
router.put("/api/foo", handler)internapage/api/index.jsnão serve esse manipulador em/api/foo.
O mesmo que .Method, mas aceita quaisquer métodos.
Crie um manipulador para lidar com solicitações recebidas.
options.onerror
Aceita uma função como um manipulador de erros de captura-total; executado sempre que um manipulador lança um erro. Por padrão, ele responde com um 500 Internal Server Error ao registrar o erro no console .
function onError ( err , req , res ) {
logger . log ( err ) ;
// OR: console.error(err);
res . status ( 500 ) . end ( "Internal server error" ) ;
}
export default router . handler ( { onError } ) ;options.onnomatch
Aceita uma função de (req, res) como um manipulador quando nenhuma rota é correspondida. Por padrão, ele responde com um status 404 e uma Route [Method] [Url] not found corpo.
function onNoMatch ( req , res ) {
res . status ( 404 ) . end ( "page is not found... or is it!?" ) ;
}
export default router . handler ( { onNoMatch } ) ; Executa req e res através da cadeia de middleware e retorna uma promessa . Ele resolve com o valor retornado dos manipuladores.
router
. use ( async ( req , res , next ) => {
return ( await next ( ) ) + 1 ;
} )
. use ( async ( ) => {
return ( await next ( ) ) + 2 ;
} )
. use ( async ( ) => {
return 3 ;
} ) ;
console . log ( await router . run ( req , res ) ) ;
// The above will print "6" Se um erro em lançamento dentro da cadeia, router.run rejeitará. Você também pode adicionar uma captura de tentativa no primeiro middleware para pegar o erro antes de rejeitar a chamada .run() :
router
. use ( async ( req , res , next ) => {
return next ( ) . catch ( errorHandler ) ;
} )
. use ( thisMiddlewareMightThrow ) ;
await router . run ( req , res ) ; Existem algumas armadilhas no uso next-connect . Abaixo estão as coisas a ter em mente para usá -lo corretamente.
await next() Se next() não for aguardado, os erros não serão capturados se forem jogados em manipuladores de assíncronos, levando à UnhandledPromiseRejection .
// OK: we don't use async so no need to await
router
. use ( ( req , res , next ) => {
next ( ) ;
} )
. use ( ( req , res , next ) => {
next ( ) ;
} )
. use ( ( ) => {
throw new Error ( "?" ) ;
} ) ;
// BAD: This will lead to UnhandledPromiseRejection
router
. use ( async ( req , res , next ) => {
next ( ) ;
} )
. use ( async ( req , res , next ) => {
next ( ) ;
} )
. use ( async ( ) => {
throw new Error ( "?" ) ;
} ) ;
// GOOD
router
. use ( async ( req , res , next ) => {
await next ( ) ; // next() is awaited, so errors are caught properly
} )
. use ( ( req , res , next ) => {
return next ( ) ; // this works as well since we forward the rejected promise
} )
. use ( async ( ) => {
throw new Error ( "?" ) ;
// return new Promise.reject("?");
} ) ;Outra questão é que o manipulador resolveria antes que todo o código em cada camada seja executado.
const handler = router
. use ( async ( req , res , next ) => {
next ( ) ; // this is not returned or await
} )
. get ( async ( ) => {
// simulate a long task
await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
res . send ( "ok" ) ;
console . log ( "request is completed" ) ;
} )
. handler ( ) ;
await handler ( req , res ) ;
console . log ( "finally" ) ; // this will run before the get layer gets to finish
// This will result in:
// 1) "finally"
// 2) "request is completed"router como o padrão abaixo: // api-libs/base.js
export default createRouter ( ) . use ( a ) . use ( b ) ;
// api/foo.js
import router from "api-libs/base" ;
export default router . get ( x ) . handler ( ) ;
// api/bar.js
import router from "api-libs/base" ;
export default router . get ( y ) . handler ( ) ; Isso ocorre porque, em cada rota da API, a mesma instância do roteador é mutada, levando a comportamentos indefinidos. Se você deseja alcançar algo assim, pode usar router.clone para retornar instâncias diferentes com as mesmas rotas preenchidas.
// api-libs/base.js
export default createRouter ( ) . use ( a ) . use ( b ) ;
// api/foo.js
import router from "api-libs/base" ;
export default router . clone ( ) . get ( x ) . handler ( ) ;
// api/bar.js
import router from "api-libs/base" ;
export default router . clone ( ) . get ( y ) . handler ( ) ;res.redirect a função de resposta getServerSideProps res.(s)end // page/index.js
const handler = createRouter ( )
. use ( ( req , res ) => {
// BAD: res.redirect is not a function (not defined in `getServerSideProps`)
// See https://github.com/hoangvvo/next-connect/issues/194#issuecomment-1172961741 for a solution
res . redirect ( "foo" ) ;
} )
. use ( ( req , res ) => {
// BAD: `getServerSideProps` gives undefined behavior if we try to send a response
res . end ( "bar" ) ;
} ) ;
export async function getServerSideProps ( { req , res } ) {
await router . run ( req , res ) ;
return {
props : { } ,
} ;
}handler() diretamente no getServerSideProps . // page/index.js
const router = createRouter ( ) . use ( foo ) . use ( bar ) ;
const handler = router . handler ( ) ;
export async function getServerSideProps ( { req , res } ) {
await handler ( req , res ) ; // BAD: You should call router.run(req, res);
return {
props : { } ,
} ;
} Por favor, veja o meu contribuindo.md.
Mit