ห้องสมุดนี้เขียนขึ้นเพื่อรองรับเส้นทาง API ที่ใช้เราเตอร์หน้าหน้า ยังไม่ได้ทดสอบกับเราเตอร์แอพ
เส้นทาง Next.js API เป็นวิธีที่สนุกและง่ายดายในการเพิ่มฟังก์ชั่นแบ็กเอนด์ลงในแอพ React อย่างไรก็ตามเมื่อถึงเวลาที่จะเพิ่มมิดเดิลแวร์ไม่มีวิธีที่ง่ายในการใช้งาน
เอกสาร Next.js อย่างเป็นทางการแนะนำให้เขียนฟังก์ชั่นภายในตัวจัดการเส้นทาง API ของคุณ นี่เป็นก้าวถอยหลังขนาดใหญ่เมื่อเทียบกับ API ที่สะอาดโดย Express.js หรือ koa.js
ห้องสมุดนี้พยายามที่จะจัดทำรูปแบบมิดเดิลแวร์ที่ไม่สามารถทำความสะอาดได้น้อยที่สุดซึ่งมีทั้งการผลิตและการใช้งานที่น่าพอใจ
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 ) ; แบบจำลองทางจิตของฉันสำหรับวิธีการที่ห้องสมุดนี้จัดการกับฟังก์ชั่นมิดเดิลแวร์เป็นของ "สแต็คที่คดเคี้ยวและคลี่คลาย"
ลองจินตนาการว่าคุณเคยใช้ label เพื่อเพิ่มฟังก์ชั่นมิดเดิลแวร์สองฟังก์ชั่นลงในเส้นทาง API
เมื่อมีการร้องขอเข้ามานี่เป็นความประทับใจที่คร่าวๆว่าคำขอนั้นจะผ่านฟังก์ชั่นมิดเดิลแวร์ทั้งหมดได้อย่างไรตัวจัดการเส้นทาง API เองและจากนั้นกลับผ่านมิดเดิลแวร์
|-----------------|-----------------|--------------------|
| Middleware #1 | Middleware #2 | API Route Handler |
|-----------------|-----------------|--------------------|
| | | |
Request ------|----> Setup -----|----> Setup -----|-->------| |
| | | | |
|-----------------|-----------------| V |
| | | |
| await next() | await next() | API stuff |
| | | |
|-----------------|-----------------| | |
| | | | |
Response <----|--- Teardown <---|--- Teardown <---|---------| |
| | | |
|-----------------|-----------------|--------------------|
ในขณะที่นี่เป็นแผนภาพ ASCII ที่แย่ แต่ฉันคิดว่ามันให้ความประทับใจที่ถูกต้อง การร้องขอจะปิดตัวลงแม้ว่ามิดเดิลแวร์แต่ละคนจะทำงานอย่างต่อเนื่อง
ทุกฟังก์ชั่นมิดเดิลแวร์มีโอกาสผ่านสามขั้นตอน:
เฟส "การตั้งค่า" ครอบคลุมทุกสิ่งที่เกิดขึ้นก่อนที่ await next() ขั้นตอน "การรอคอย" เป็นเพียงแค่ await next() เฟส "teardown" คือรหัสที่เหลืออยู่ภายในฟังก์ชั่นมิดเดิลแวร์หลังจาก await next()
เป็นที่น่าสังเกตว่าแม้ว่าเฟสเหล่านี้จะมีให้สำหรับฟังก์ชั่นมิดเดิลแวร์ทั้งหมด แต่คุณไม่จำเป็นต้องใช้ประโยชน์จากพวกเขาทั้งหมด
ตัวอย่างเช่นในข้อผิดพลาดในการจับมิดเดิลแวร์คุณอาจห่อ await next() ในบล็อก try / catch ในทางกลับกันคุณอาจมีการร้องขอมิดเดิลแวร์กำหนดเวลาที่จับเวลาเริ่มต้นในระหว่างขั้นตอนการตั้งค่ารอแล้วจับเวลาเสร็จสิ้นในขั้นตอนการฉีกขาด
labelนี่คือยูทิลิตี้หลักสำหรับการสร้างคอลเลกชันที่มีคุณค่าของมิดเดิลแวร์สำหรับใช้ตลอดเส้นทาง next.js API
const withMiddleware = label ( middleware , defaults ) ; middleware : วัตถุที่มีฟังก์ชั่นมิดเดิลแวร์หรืออาร์เรย์ของมิดเดิลแวร์defaults : (ไม่บังคับ) อาร์เรย์ของคีย์ middleware ที่จะถูกเรียกใช้โดยอัตโนมัติ label ส่งคืนฟังก์ชั่น (อ้างถึงตามปกติว่า withMiddleware ) ที่ใช้ Currying เพื่อรับรายการชื่อมิดเดิลแวร์ที่จะเรียกใช้ตามด้วยฟังก์ชันตัวจัดการ API API ถัดไป
โดยทั่วไปแล้วจะนำเข้า withMiddleware ในไฟล์เส้นทาง API และใช้ในคำสั่งส่งออกเริ่มต้น:
import { withMiddleware } from "../helpers/my-middleware" ;
const apiRouteHandler = async ( req , res ) => {
...
}
export default withMiddleware ( "foo" , "bar" , "baz" ) ( apiRouteHandler ) ; แม้ว่า label อาจมีฟังก์ชั่นมิดเดิลแวร์จำนวนมาก แต่มิดเดิลแวร์ที่เกิดขึ้นจริงที่เรียกใช้โดยเส้นทาง API จะถูกกำหนดโดยชื่อที่ส่งผ่านไปยัง 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 ยูทิลิตี้นี้ยอมรับฟังก์ชั่นมิดเดิลแวร์โดยตรงและดำเนินการทั้งหมดตามลำดับ เป็นทางเลือกที่ง่ายกว่าสำหรับ label ที่มีประโยชน์สำหรับการจัดการฟังก์ชั่นมิดเดิลแวร์แบบครั้งเดียว
const withInlineMiddleware = use ( ... middleware ) ; middleware : รายการฟังก์ชั่นมิดเดิลแวร์และ/หรืออาร์เรย์ของฟังก์ชั่นมิดเดิลแวร์ use การส่งคืนฟังก์ชั่นที่ยอมรับตัวจัดการเส้นทาง API Next.js
import { use } from "next-api-middleware" ;
import cors from "cors" ;
const apiRouteThatOnlyNeedsCORS = async ( req , res ) => {
...
}
export default use ( cors ( ) ) ( apiRouteThatOnlyNeedsCORS ) ; ดูตัวอย่าง MD สำหรับตัวอย่างรายละเอียดเพิ่มเติมของ label และ use
เนื่องจาก use และ label ยอมรับค่าที่ประเมินไปยังฟังก์ชั่นมิดเดิลแวร์สิ่งนี้ให้โอกาสในการสร้างโรงงานมิดเดิลแวร์ที่กำหนดเอง
นี่คือตัวอย่างของโรงงานที่สร้างฟังก์ชั่นมิดเดิลแวร์เพื่ออนุญาตเฉพาะคำขอด้วยวิธี HTTP ที่กำหนดเท่านั้น:
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 ได้รับแรงบันดาลใจจากสไตล์มิดเดิลแวร์แบบอะซิงโครนัสที่ได้รับความนิยมจาก koa.js
type Middleware < Request = NextApiRequest , Response = NextApiResponse > = (
req : Request ,
res : Response ,
next : ( ) => Promise < void >
) => Promise < void > ;