จัดการ Request ใน Next.js ให้ง่ายกว่าเดิมด้วยการทำ Middleware Stack

Web Development

จัดการ Request ใน Next.js ให้ง่ายกว่าเดิมด้วยการทำ Middleware Stack

ประมาณ 1 เดือนที่ผ่านมา

2 min read

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.json

Result

เสร็จแล้วเรามาดูผลงานกันหน่อย ก็จะเห็นว่า middleware ของเราจะทํางานตามที่เราต้องการ

ก็ลองนำไปใช้กันดู ส่วนตัวอยู่กับ middleware ค่อนข้างมาก เพราะต้องจัดการ request แต่ละ route แต่ก็ได้แต่เกิดคำถามว่า ทำไมเราต้องทำไว้ที่ไฟล์เดียว แยกหน้าที่กัน เพื่อจัดการให้มันง่ายกว่าเดิมได้ไหม ก็เลยก็เป็นบทความนี้ขึ้นมา

Reference

Tags:

Next.js