+1

Thêm Authorization header khi gọi API với Next.js

Khi làm việc với JWT trong Next.js (App Router), việc tự động gắn Authorization header vào mọi API request là cực kỳ quan trọng. Bài viết này sẽ hướng dẫn một trong những cách triển khai tính năng với kiến trúc phân tách môi trường rõ ràng.
Tham khảo source code mẫu ở Github: nghiepdev/nextjs-authentication

1. Thiết lập cấu trúc project với pnpm workspace

Sử dụng monorepo pattern để quản lý các môi trường khác nhau:

/app
├── page.tsx
├── layout.tsx
/packages/client
├── browser.ts    // Xử lý logic cho trình duyệt
├── node.ts       // Dành cho server-side (không dùng cho auth)
├── package.json 
├── rsc.ts        // Xử lý React Server Components, Server Action, Route Handlers
└── shared.ts     // Logic chung

Cấu hình import linh hoạt:

import { apiClient } from '@app/client';

2. Xử lý cho React Server Components (RSC)

Với RSC, chúng ta có thể truy cập cookies trực tiếp:

import {cookies} from 'next/headers';
import {baseClient} from './shared';

export const apiClient = baseClient.extend({
  prefixUrl: process.env.NEXT_PUBLIC_API_URL,
  hooks: {
    beforeRequest: [
      async (request) => {
        const accessToken = (await cookies()).get('session')?.value;
        if (accessToken) {
          request.headers.set('Authorization', `Bearer ${accessToken}`);
        }
      },
    ],
  },
});

3. Xử lý cho môi trường Browser

Do hạn chế của HTTP-only cookies, chúng ta cần 2 bước:

3.0 Thay đổi endpoit /backend

// browser.ts
import {baseClient} from './shared';

export const client = baseClient.extend({
  prefixUrl: '/backend',
});

3.1. Thiết lập API Proxy trong next.config.ts

// next.config.ts
module.exports = {
  async rewrites() {
    return {
      afterFiles: [
        {
          source: '/backend/:path*',
          destination: `${process.env.NEXT_PUBLIC_API_URL}/:path*`,
        },
      ],
    };
  },
}

3.2. Middleware xử lý gắn Authorization header

// middleware.ts
export async function middleware(req: NextRequest) {
  const path = req.nextUrl.pathname;
  const accessToken = req.cookies.get('session')?.value;
  
  if (accessToken && path.startsWith('/backend')) {
    const headers = new Headers(req.headers);
    headers.set('Authorization', `Bearer ${accessToken}`);
    return NextResponse.next({
      request: { headers }
    });
  }
  return NextResponse.next();
}

4. Tại sao không dùng SSR cho authentication?

  • Không truy cập được cookies/headers trực tiếp

5. Cách sử dụng thống nhất

5.1 Dynamic: RSC, Server Action, Route Handlers

import { apiClient } from '@app/client';

export default async function Page() {
   await apiClient.get('/user');
   return <div>...</div>;
}

5.2 Client Components: 'use client'

Ví dụ sử dụng với swr hoặc react-query

// use-session.ts
import useSWR from 'swr';
import {apiClient} from '@app/client';

const fetcher = () => {
  return apiClient.get('/user').json();
};

export function useSession() {
  return useSWR('session', fetcher);
}

Lợi ích chính:

✅ Bảo mật với HTTP-only cookies
✅ Tách biệt logic cho từng môi trường
✅ Dễ dàng maintain và mở rộng

Có thể tham khảo source code mẫu ở Github: nghiepdev/nextjs-authentication


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí