Works/2025/pinit
Live in production
NextJS
2025
Updated Aug 2025

pinit.

Portfolio kiêm nơi tổng hợp kiến thức: 30 bài viết, 13 bài system design, 109 thuật ngữ. Tự dựng cả chuỗi từ git push tới production trên VPS bằng Jenkins, Docker và ghcr.

shipped · production
▦ /2025 · NextJS
Shipped · NextJS
02

Project narrative.

from the maker

Bối cảnh

Portfolio kiêm nơi tổng hợp kiến thức: 30 bài viết, 13 bài system design, 109 thuật ngữ. Tự dựng toàn bộ chuỗi từ code tới production thay vì đẩy lên hosting dựng sẵn, để hiểu từng mắt xích.

Next.js 15 (pages router) · React 19 · MUI 5 · TypeScript. Nội dung bài viết đọc từ markdown trong repo; dữ liệu project lấy từ API json-server-blog tách riêng.

Vấn đề

Ba ràng buộc định hình kiến trúc:

  • Build không được phụ thuộc backend. API nằm ở service riêng; nếu nó chết lúc build thì trang vẫn phải dựng được.
  • Deploy lên VPS tự quản, không dùng PaaS — tự lo image, registry, rollout.
  • Nội dung nặng không được kéo vào bundle chung. 13 bài system design (~2000 dòng) và 109 thuật ngữ mà lọt vào chunk dùng chung thì mọi trang đều gánh, kể cả trang chủ.

Giải pháp

git push json-server-blog API Jenkins local docker build amd64 ghcr.io VPS: docker compose nginx

Chống phụ thuộc backend. Mọi lời gọi API đi qua safeFetchJson — lỗi mạng trả null thay vì ném, chỗ gọi tự fallback về mảng rỗng. SSG + ISR (revalidate) nên trang phục vụ từ bản tĩnh, dữ liệu tự làm mới nền.

Đóng gói. Dockerfile multi-stage trên node:20-alpine: depsbuilderrunner. Container chạy bằng user thường (uid 1001) chứ không phải root. Dùng output: standalone của Next nên image chỉ chứa đúng file cần chạy.

Đường ra production. Jenkins chạy ngay trên máy local, tách hai môi trường theo branch — staging cổng 3001, prod cổng 3000. Image gắn tag <env>-<build-number> để biết chính xác bản nào đang chạy. Bước đẩy lên ghcr có retry 3 lần vì đăng nhập registry thỉnh thoảng lỗi keychain.

Giữ bundle gọn. Barrel constants/index.ts cố ý không re-export dữ liệu system design, kèm test canh để lỗi này không tái phát — đây là lỗi đã từng xảy ra và không công cụ nào trong repo bắt được, chỉ đọc kích thước chunk trong output next build mới thấy.

Kết quả

  • 26 route, phần lớn dựng tĩnh
  • 156 test trong 8 file, chạy khoảng 2 giây
  • Trang chủ 341 kB First Load JS, phần dùng chung 249 kB
  • Dữ liệu system design nằm ngoài chunk của trang chủ — đối chiếu trực tiếp bằng danh sách chunk mà trang chủ nạp
  • Đang chạy thật trên VPS sau nginx
03

Architecture flow.

3 layers · swappable
01
Layer

Frontend layer

Next.js 15 pages router, SSG + ISR
React 19
MUI 5 cho hệ thống giao diện
TypeScript
02
Layer

API / orchestration layer

Jenkins chạy CI/CD ngay trên máy local
Docker multi-stage, container chạy non-root
ghcr.io lưu image
docker compose trên VPS, nginx đứng trước
03
Layer

Data layer

Bài viết đọc từ markdown trong repo
Dữ liệu project lấy từ API json-server-blog
04

Technical stack.

5 tags
NextJS
TypeScript
Docker
ReactJS
ReactJS
05

Đọc thêm.

3 bài cùng chủ đề