Kiến Trúc Next.js App Router + NestJS Cho Dự Án Enterprise 2026

Nguyễn Lê Đình Tiên

Nguyễn Lê Đình Tiên

17 tháng 9, 2026

Kiến Trúc Next.js App Router + NestJS Cho Dự Án Enterprise 2026
# Next.js App Router + NestJS Architecture

## Mục tiêu

Xây dựng hệ thống:

```txt
Browser
    ↓
Next.js App Router
    ↓
NestJS API
    ↓
Database
```

Yêu cầu:

- FE không lưu logic business.
- NestJS là hệ thống backend duy nhất.
- Không tạo thêm API BFF riêng để aggregate dữ liệu.
- Next.js chỉ đóng vai trò:
  - Render UI
  - Quản lý session
  - Gọi NestJS
  - Xử lý Authentication
- JWT không bị lộ ra JavaScript phía client.
- Hỗ trợ SSR và Client-side Fetch.

---

# Kiến trúc tổng thể

```txt
┌─────────────┐
│   Browser   │
└──────┬──────┘
       │
       │ HttpOnly Cookie
       ▼
┌─────────────┐
│   Next.js   │
│ App Router  │
└──────┬──────┘
       │
       │ Authorization: Bearer xxx
       ▼
┌─────────────┐
│   NestJS    │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ Database    │
└─────────────┘
```

---

# Cấu trúc thư mục đề xuất

```txt
src/
│
├── app/
│   ├── login/
│   │   └── page.tsx
│   │
│   ├── dashboard/
│   │   ├── page.tsx
│   │   └── loading.tsx
│   │
│   └── profile/
│       └── page.tsx
│
├── modules/
│   ├── auth/
│   │   ├── auth.service.ts
│   │   └── auth.types.ts
│   │
│   ├── user/
│   │   ├── user.service.ts
│   │   ├── user.mapper.ts
│   │   ├── user.types.ts
│   │   └── hooks/
│   │       └── useUser.ts
│   │
│   └── dashboard/
│       ├── dashboard.service.ts
│       ├── dashboard.mapper.ts
│       └── hooks/
│           └── useDashboard.ts
│
├── lib/
│   ├── api/
│   │   ├── api-client.ts
│   │   └── fetch-wrapper.ts
│   │
│   ├── auth/
│   │   ├── get-token.ts
│   │   ├── refresh-token.ts
│   │   └── logout.ts
│   │
│   └── constants/
│
├── components/
│   ├── ui/
│   ├── layout/
│   └── common/
│
├── middleware.ts
│
└── types/
```

---

# Nguyên tắc

## Không gọi NestJS trực tiếp trong Component

Không nên:

```tsx
export default async function Page() {
  const data = await fetch(
    "https://api.company.com/users"
  );

  ...
}
```

Nên:

```tsx
const user = await userService.getProfile();
```

---

# Service Layer

Ví dụ:

```txt
modules/user/user.service.ts
```

```ts
export async function getProfile() {
    return apiClient.get("/users/profile");
}
```

Mọi màn hình đều đi qua Service Layer.

---

# API Client

Tạo một nơi duy nhất quản lý request.

```txt
lib/api/api-client.ts
```

Ví dụ trách nhiệm:

- thêm JWT
- refresh token
- xử lý 401
- logging
- timeout
- retry

Toàn bộ project chỉ gọi:

```ts
apiClient.get()
apiClient.post()
apiClient.put()
apiClient.delete()
```

---

# Luồng Login

## Bước 1

User login:

```txt
Email
Password
```

↓

```txt
POST /auth/login
```

↓

NestJS trả về:

```json
{
  "accessToken": "...",
  "refreshToken": "..."
}
```

---

## Bước 2

Next.js lưu token vào Cookie

```txt
access_token
refresh_token
```

Thuộc tính:

```txt
HttpOnly
Secure
SameSite=Lax
```

JavaScript không đọc được.

---

# Request Flow

Sau khi login:

```txt
Browser
    ↓
Cookie
    ↓
Next.js
    ↓
NestJS
```

Khi gọi API:

```txt
Page
 ↓
userService
 ↓
apiClient
 ↓
NestJS
```

---

# JWT Handling

## Access Token

Mục đích:

```txt
Authentication
```

Thời gian sống:

```txt
15 phút
hoặc
30 phút
```

Ví dụ:

```txt
exp: 15m
```

---

## Refresh Token

Mục đích:

```txt
Tạo access token mới
```

Thời gian sống:

```txt
7 ngày
14 ngày
30 ngày
```

Ví dụ:

```txt
exp: 7d
```

---

# SSR Authentication Flow

Server Component:

```txt
Page Load
    ↓
Đọc cookie
    ↓
Lấy access token
    ↓
Call NestJS
```

Nếu thành công:

```txt
200
```

↓

Render UI.

---

# Xử lý 401

Khi access token hết hạn:

```txt
Next.js
    ↓
NestJS
    ↓
401
```

Không redirect ngay.

Thực hiện:

```txt
refresh token
```

---

# Refresh Flow

```txt
Call API
    ↓
401
    ↓
Call Refresh API
    ↓
Nhận Access Token mới
    ↓
Gọi lại API cũ
```

Flow:

```txt
Page
 ↓
NestJS
 ↓
401

 ↓

POST /auth/refresh

 ↓

New Access Token

 ↓

Retry Request

 ↓

200
```

User không nhận thấy quá trình này.

---

# Refresh Success

```txt
401
 ↓
Refresh thành công
 ↓
Retry
 ↓
200
```

UI vẫn hoạt động bình thường.

---

# Refresh Fail

Nếu:

```txt
Refresh Token hết hạn
```

hoặc

```txt
Refresh Token không hợp lệ
```

thì:

```txt
401
 ↓
Refresh
 ↓
401
 ↓
Logout
 ↓
Redirect Login
```

---

# Logout Flow

```txt
User Click Logout
```

↓

```txt
POST /auth/logout
```

↓

Xóa:

```txt
access_token
refresh_token
```

↓

```txt
Redirect /login
```

---

# Middleware

Chỉ xử lý:

```txt
Có cookie hay không
```

Ví dụ:

```txt
Không có access_token
```

↓

```txt
Redirect Login
```

Middleware không nên:

- refresh token
- gọi NestJS API
- thực hiện business logic

---

# Client Side Fetch

Sau khi page render:

```txt
User Click Filter
```

hoặc

```txt
User Search
```

có thể dùng:

```txt
TanStack Query
```

Flow:

```txt
Browser
 ↓
Next.js
 ↓
NestJS
```

Vẫn sử dụng chung:

```txt
apiClient
```

để đồng bộ cơ chế JWT.

---

# UI DTO Mapping

Không để UI phụ thuộc trực tiếp response từ backend.

Ví dụ NestJS:

```json
{
  "id": 1,
  "name": "Tien",
  "email": "abc@gmail.com",
  "address": "...",
  "roles": []
}
```

Mapper:

```ts
UserProfileDto
```

```json
{
  "name": "Tien"
}
```

Component chỉ dùng:

```ts
UserProfileDto
```

Điều này giúp:

- FE không phụ thuộc BE
- dễ thay đổi API
- dễ test
- dễ maintain

---

# Best Practices

✅ HttpOnly Cookie

✅ Access Token ngắn hạn

✅ Refresh Token dài hạn

✅ Service Layer cho toàn bộ request

✅ Một apiClient duy nhất

✅ Refresh tự động khi gặp 401

✅ Redirect login khi refresh thất bại

✅ DTO riêng cho UI

✅ TanStack Query cho client interaction

✅ Server Component cho dữ liệu SSR

---

# Kết luận

Kiến trúc đề xuất:

```txt
Browser
    ↓
Next.js App Router
    ↓
Service Layer
    ↓
Api Client
    ↓
NestJS
    ↓
Database
```

Trong mô hình này:

- NestJS là backend duy nhất.
- Không cần tRPC.
- Không cần BFF API Route.
- JWT được quản lý tập trung.
- FE không chứa logic auth phức tạp.
- Dễ mở rộng cho dự án lớn nhiều màn hình.

Bài viết liên quan

Kiến Trúc Next.js App Router + NestJS Cho Dự Án Enterprise 2026 | Nguyễn Lê Đình Tiên Portfolio