middleware ใน Next.js เวอร์ชั่นหลังๆ นี้มี middleware ให้ใช้แค่ไฟล์ เดียว ทำให้บางทีถ้าเรามี logic เยอะ จะยากต่อการจัดการมาก จะดีกว่าไหมถ้าเราสามารถแยก middleware แต่ละ logic ไปอีกไฟล์นึงได้
สำหรับถ้าใครไม่รู้ว่า Middleware มันคืออะไร ก็อธิบายสั้นๆ เลย Middleware คือสิ่งที่คั่นกลางระหว่างสิ่งที่ user ขอมา กับ app เรา ว่าถ้าคุณอยากเข้า route นี้ คุณต้องมีอะไรบ้าง ก็นั่นแหละหลักการมันแค่นั้นเลย
พร้อมแล้วก็ไปลงมือกันเลย
Create middlewares Directory
เริ่มจากเรา folder middlewares ไว้ใน src สำหรับเก็บ Middleware ของเรา
Create Middleware Stack File
สร้างไฟล์ stackHandler ใน folder middlewares เพื่อใช้สำหรับเป็นเหมือน root ของ middleware หรือเข้าใจง่ายๆ stackHandler ก็คือทำหน้าที่รวม middleware ทั้งหมด
import { NextMiddleware, NextResponse } from "next/server";
export function stackMiddlewares(
functions: MiddlewareFactory[] = [],
index = 0,
): NextMiddleware {
const current = functions[index];
if (current) {
const next = stackMiddlewares(functions, index + 1);
return current(next);
}
return () => NextResponse.next();
}Create Another Middleware File
ถึงเวลาสร้าง middleware ที่เราจะใช้งาน ในที่นี้ผมจะสร้าง middleware ชื่อ withUser
โดย logic ก็ง่ายๆ ถ้าเข้า path /profile แล้วส่ง userId ไปด้วย เช่น /profile/1 ก็จะให้ผ่านได้ แต่ถ้าไม่ ก็จะ redirect ไปที่ /login
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
export const withUser: MiddlewareFactory = (next) => {
return async (request: NextRequest, _next: NextFetchEvent) => {
const pathname = request.nextUrl.pathname;
if (["/profile"]?.some((path) => pathname.startsWith(path))) {
/**
* * fake credentials with parameter
*/
const userId = request.nextUrl.pathname.split("/")[2];
if (!userId) {
/**
* * if not have query userId, redirect to login
*/
const url = new URL(`/login`, request.url);
return NextResponse.redirect(url);
}
}
return next(request, _next);
};
};Create Middleware File
สุดท้ายก็สร้าง middleware.ts ใน src ตามฉบับที่ Next.js อยากได้
import { stackMiddlewares } from "@/middlewares/stackHandler";
import { withUser } from "@/middlewares/withUser";
const middlewares = [withUser];
export default stackMiddlewares(middlewares);Create Type
สำหรับ TypeScript ก็สร้าง Type ให้ MiddlewareFactory ด้วย
import { NextMiddleware } from "next/server";
type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;Project Structure
เรามาดู structure ไฟล์กันหน่อย ก็จะได้หน้าตาประมาณนี้ ส่วน folder app ขอไม่ลงรายละเอียดแล้วกัน เพราะไม่มีอะไร คิดว่าน่าจะใช้เป็นกันอยู่แล้ว 😉
.
├── src
│ ├── app
│ │ ├── favicon.ico
│ │ ├── layout.tsx
│ │ ├── login
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── profile
│ │ └── [id]
│ │ └── page.tsx
│ ├── middleware.ts
│ ├── middlewares
│ │ ├── stackHandler.ts
│ │ └── withUser.ts
│ └── types
│ └── middleware.d.ts
└── tsconfig.jsonResult
เสร็จแล้วเรามาดูผลงานกันหน่อย ก็จะเห็นว่า middleware ของเราจะทํางานตามที่เราต้องการ
ก็ลองนำไปใช้กันดู ส่วนตัวอยู่กับ middleware ค่อนข้างมาก เพราะต้องจัดการ request แต่ละ route แต่ก็ได้แต่เกิดคำถามว่า ทำไมเราต้องทำไว้ที่ไฟล์เดียว แยกหน้าที่กัน เพื่อจัดการให้มันง่ายกว่าเดิมได้ไหม ก็เลยก็เป็นบทความนี้ขึ้นมา