Bài 34.1: Deploying Next.js-TypeScript apps

Bài 34.1: Deploying Next.js-TypeScript apps
Chào mừng bạn quay trở lại với chuỗi bài viết về phát triển web front-end! Sau hành trình xây dựng những ứng dụng Next.js mạnh mẽ với sự hỗ trợ của TypeScript, giờ là lúc biến thành quả của chúng ta thành một sản phẩm sống, có thể truy cập được từ bất kỳ đâu trên thế giới. Bước cuối cùng đầy kỳ diệu này chính là triển khai ứng dụng (deployment).
Triển khai ứng dụng Next.js không chỉ đơn thuần là "đặt file lên server". Next.js là một framework full-stack với khả năng Server-Side Rendering (SSR), Static Site Generation (SSG), và API Routes chạy dưới dạng Serverless Functions hoặc trên một Node.js server truyền thống. Do đó, quy trình deployment cần đảm bảo rằng tất cả các khía cạnh này đều hoạt động chính xác. Việc sử dụng TypeScript trong dự án của chúng ta cũng mang lại những lợi ích nhất định trong quá trình này, chủ yếu tập trung vào giai đoạn build (xây dựng) trước khi triển khai.
Hãy cùng khám phá các phương pháp phổ biến để đưa ứng dụng Next.js-TypeScript của bạn lên mạng!
Quá trình "Build" - Nền tảng của Deployment
Trước khi nói về việc đặt ứng dụng ở đâu, chúng ta cần hiểu quá trình chuẩn bị ứng dụng. Với các ứng dụng front-end hiện đại như Next.js, đặc biệt khi sử dụng TypeScript, chúng ta không chỉ sao chép mã nguồn lên server. Thay vào đó, chúng ta thực hiện quá trình build (xây dựng).
Lệnh next build
là trái tim của giai đoạn chuẩn bị này. Lệnh này sẽ:
- Kiểm tra mã nguồn của bạn, bao gồm cả việc kiểm tra kiểu (type checking) nếu bạn dùng TypeScript. Nếu có lỗi TypeScript, quá trình build sẽ dừng lại, giúp bạn phát hiện sớm các vấn đề tiềm ẩn.
- Biên dịch mã TypeScript/JavaScript, React, và các tài nguyên khác (CSS, hình ảnh...).
- Tối ưu hóa mã nguồn (minification, code splitting...).
- Tạo ra các file đầu ra sẵn sàng để phục vụ (HTML tĩnh, các chunk JavaScript, các API routes biên dịch...).
Output của quá trình build thường nằm trong thư mục .next
. Đây chính là những gì chúng ta sẽ cần để triển khai ứng dụng.
Các Nền tảng Triển khai Phổ biến
Có nhiều lựa chọn để triển khai ứng dụng Next.js, từ các dịch vụ chuyên biệt cho front-end đến việc tự quản lý server. Mỗi lựa chọn có ưu và nhược điểm riêng.
1. Triển khai với Vercel
Vercel là nền tảng được tạo ra bởi chính đội ngũ phát triển Next.js. Do đó, nó cung cấp sự tích hợp sâu sắc nhất và là lựa chọn đơn giản nhất trong hầu hết các trường hợp.
Tại sao chọn Vercel?
- Tích hợp liền mạch: Tự động nhận diện dự án Next.js, cấu hình build, và deploy mọi tính năng (SSR, SSG, API Routes dưới dạng Serverless Functions, Edge Functions) mà không cần cấu hình phức tạp.
- Hiệu suất cao: Sử dụng mạng lưới Edge Network toàn cầu để phục vụ nội dung nhanh chóng.
- Tự động: Tự động deploy mỗi khi bạn push code lên Git branch đã kết nối. Cung cấp các bản xem trước (Preview Deployments) cho mỗi pull request.
- Miễn phí: Có gói Free tier rất hào phóng cho các dự án cá nhân và phi thương mại.
Các bước cơ bản:
- Kết nối Git Repository: Đăng nhập vào Vercel bằng tài khoản Git (GitHub, GitLab, Bitbucket). Chọn repository chứa dự án Next.js của bạn.
- Cấu hình dự án: Vercel sẽ tự động phát hiện Next.js. Bạn chỉ cần xác nhận các cài đặt cơ bản như root directory (thường là
/
), build command (next build
), và output directory (.next
). - Cấu hình biến môi trường (Environment Variables): Nếu ứng dụng của bạn sử dụng biến môi trường (ví dụ: kết nối database, khóa API), bạn có thể thêm chúng trực tiếp trong dashboard của Vercel.
Ví dụ, nếu bạn có file .env.local
trong dự án:
# .env.local DB_CONNECTION_STRING=your_db_connection_string API_KEY=your_secret_api_key
Bạn sẽ cần thêm các biến DB_CONNECTION_STRING
và API_KEY
vào cài đặt môi trường trong dashboard Vercel. Vercel sẽ cung cấp các biến này cho quá trình build và runtime của ứng dụng.
// Trong mã nguồn Next.js (ví dụ: trong getStaticProps, getServerSideProps, API Routes)
// Các biến môi trường chỉ có trên server (runtime) hoặc trong quá trình build
const dbConnectionString = process.env.DB_CONNECTION_STRING;
// Đối với biến cần dùng ở client-side (phải bắt đầu bằng NEXT_PUBLIC_)
// Ví dụ trong .env.local: NEXT_PUBLIC_ANALYTICS_ID=abcde12345
// Bạn có thể truy cập: process.env.NEXT_PUBLIC_ANALYTICS_ID
Giải thích: Next.js hỗ trợ biến môi trường. Biến không bắt đầu bằng NEXT_PUBLIC_
chỉ có sẵn ở môi trường Node.js (server-side/API Routes/build time), còn biến có tiền tố NEXT_PUBLIC_
sẽ được bundle vào mã client-side (chú ý: không dùng cho các thông tin nhạy cảm!). Vercel cho phép bạn cấu hình các biến này một cách an toàn qua giao diện web hoặc Vercel CLI.
Sau khi cấu hình, Vercel sẽ tự động build và deploy ứng dụng của bạn. Mọi push tiếp theo lên branch chính sẽ kích hoạt một deployment mới.
2. Triển khai với Netlify
Netlify là một nền tảng hosting phổ biến khác cho các ứng dụng front-end tĩnh và dynamic. Nó cũng cung cấp trải nghiệm deployment rất tốt cho Next.js.
Tại sao chọn Netlify?
- Dễ sử dụng: Tương tự Vercel, Netlify tích hợp tốt với Git và tự động hóa quy trình build/deploy.
- Mạng lưới CDN mạnh mẽ: Phục vụ các file tĩnh và nội dung SSG rất nhanh.
- Hỗ trợ Serverless Functions: Netlify Functions cho phép chạy API Routes.
- Add-ons hữu ích: Forms, Identity, Split Testing...
Các bước cơ bản:
- Kết nối Git Repository: Đăng nhập vào Netlify bằng tài khoản Git và chọn repo của bạn.
- Cấu hình Build: Netlify cũng thường tự động nhận diện Next.js. Bạn cần xác nhận:
- Build command:
next build
- Publish directory: Mặc định Next.js build ra
.next
. Tuy nhiên, để Netlify hỗ trợ đầy đủ SSR/API Routes, bạn cần sử dụng@netlify/plugin-nextjs
. Plugin này sẽ xử lý output củanext build
và cấu hình Netlify Functions phù hợp. Khi dùng plugin, publish directory vẫn có thể là.next
hoặc plugin sẽ chỉ định lại.
- Build command:
Để sử dụng plugin, bạn cần tạo file netlify.toml
ở thư mục gốc của dự án:
# netlify.toml
[build]
command = "next build"
publish = ".next" # hoặc thư mục plugin chỉ định, thường không cần khai báo nếu dùng plugin
[[plugins]]
package = "@netlify/plugin-nextjs"
Giải thích: File netlify.toml
dùng để cấu hình Netlify cho dự án của bạn. Mục [build]
định nghĩa lệnh build và thư mục chứa output. Mục [[plugins]]
khai báo các plugin Netlify cần sử dụng. Plugin @netlify/plugin-nextjs
là quan trọng để Netlify hiểu và chạy đúng các tính năng dynamic của Next.js (SSR, API Routes) bằng cách chuyển chúng thành Netlify Functions.
- Cấu hình biến môi trường: Tương tự Vercel, bạn cấu hình trong dashboard Netlify hoặc dùng file
.env
trong dự án (với Netlify Build).
Netlify cũng sẽ tự động build và deploy sau khi bạn push code, cung cấp các tính năng preview và quản lý branch.
3. Triển khai lên Node.js Server tự quản lý
Nếu bạn cần kiểm soát hoàn toàn môi trường server, hoặc triển khai lên các nền tảng IaaS (Infrastructure as a Service) như AWS EC2, DigitalOcean Droplets, Azure VMs, bạn có thể chạy ứng dụng Next.js như một ứng dụng Node.js thông thường.
Tại sao chọn Self-hosting?
- Kiểm soát hoàn toàn: Bạn quản lý hệ điều hành, cấu hình server, phần mềm, bảo mật...
- Phù hợp với hạ tầng hiện có: Nếu công ty bạn đã có sẵn server hoặc cloud infrastructure.
- Linh hoạt: Có thể tích hợp sâu với các dịch vụ nội bộ khác.
Nhược điểm:
- Phức tạp hơn: Bạn phải tự lo mọi thứ: cài đặt Node.js, cấu hình server, quản lý tiến trình (process management), load balancing, SSL, giám sát...
- Tốn kém thời gian và chi phí: Đòi hỏi kiến thức về quản trị server và bảo trì liên tục.
Các bước cơ bản:
Build ứng dụng: Chạy lệnh
next build
trên máy local hoặc trên server.npm run build # hoặc yarn build
Lệnh này sẽ tạo ra thư mục
.next
.Sao chép file: Copy thư mục
.next
, thư mụcpublic
,package.json
,package-lock.json
(hoặcyarn.lock
),next.config.js
(nếu có), và bất kỳ file server custom nào (nếu bạn không dùng server mặc định của Next.js) lên server của bạn.Cài đặt dependencies: Trên server, di chuyển vào thư mục dự án và cài đặt các gói dependencies chỉ cho môi trường production.
npm install --production # hoặc yarn install --production --frozen-lockfile
Chạy ứng dụng: Next.js cung cấp lệnh
next start
để chạy ứng dụng ở chế độ production sau khi đã build.npm start # hoặc yarn start
Lệnh
npm start
thường được cấu hình sẵn trongpackage.json
:// package.json "scripts": { "dev": "next dev", "build": "next build", "start": "next start", // <-- Đây là lệnh chạy production server "lint": "next lint" },
Giải thích: Lệnh next start
sẽ khởi động một HTTP server bằng Node.js, sử dụng các file đã build trong thư mục .next
để phục vụ ứng dụng. Nó xử lý cả các request SSR, API Routes và các file tĩnh.
Quản lý tiến trình (Process Management): Lệnh
next start
sẽ dừng lại nếu server khởi động lại hoặc gặp lỗi. Trong môi trường production, bạn cần sử dụng một công cụ quản lý tiến trình như PM2 hoặc Forever để đảm bảo ứng dụng luôn chạy và tự khởi động lại khi cần thiết.Ví dụ với PM2:
# Cài đặt PM2 toàn cục npm install -g pm2 # Khởi chạy ứng dụng Next.js với PM2 pm2 start npm --name "my-next-app" -- start # Hoặc nếu dùng yarn: # pm2 start yarn --name "my-next-app" -- start # Lưu cấu hình hiện tại để PM2 tự khởi động lại khi server boot pm2 save # Khởi động PM2 khi server boot (lệnh này được tạo ra bởi pm2 save) # pm2 startup
Giải thích: PM2 giữ cho tiến trình Node.js của bạn chạy ngầm, tự động khởi động lại nếu nó crash, và có thể quản lý nhiều ứng dụng cùng lúc.
pm2 start npm -- start
(hoặcpm2 start yarn -- start
) nói với PM2 chạy lệnhnpm start
(hoặcyarn start
) trong thư mục hiện tại.Reverse Proxy: Để xử lý các tác vụ như SSL/HTTPS, nén Gzip, caching, và phục vụ các file tĩnh trực tiếp (hiệu quả hơn Node.js), bạn nên đặt một reverse proxy phía trước ứng dụng Node.js của mình. Các lựa chọn phổ biến là Nginx hoặc Caddy. Ứng dụng Node.js thường chạy trên một port nội bộ (mặc định 3000 với
next start
), và reverse proxy sẽ lắng nghe ở port 80/443, chuyển tiếp request đến ứng dụng Node.js.Cấu hình Nginx (ví dụ):
server { listen 80; server_name your-domain.com; # Redirect HTTP to HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; # Cấu hình SSL (certbot hoặc tự cấu hình) ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # ... các cấu hình SSL khác location / { proxy_pass http://localhost:3000; # Port ứng dụng Next.js đang chạy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; # ... các cấu hình proxy khác } # Có thể cấu hình để Nginx tự phục vụ các file tĩnh trong thư mục public/_next/static location /_next/static { alias /path/to/your/app/.next/static; expires 1y; access_log off; add_header Cache-Control "public, max-age=31536000, immutable"; } location /static { # Nếu bạn có thư mục static trong public alias /path/to/your/app/public/static; expires 1y; access_log off; add_header Cache-Control "public, max-age=31536000, immutable"; } }
Giải thích: Nginx lắng nghe trên port 80 và 443. Nó xử lý SSL, sau đó chuyển tiếp (proxy) các request đến ứng dụng Next.js đang chạy trên
localhost:3000
. Cấu hình cho thư mục_next/static
vàpublic/static
giúp Nginx phục vụ trực tiếp các file này với cache mạnh mẽ, giảm tải cho Node.js server.
Quy trình self-hosting yêu cầu kiến thức về Linux server, networking, và quản lý tiến trình. Nó phù hợp khi bạn cần kiểm soát cao nhất hoặc tích hợp sâu vào hạ tầng hiện có.
4. Triển khai với Docker
Docker cung cấp một cách đóng gói ứng dụng và môi trường của nó thành các "container" di động. Điều này giúp đảm bảo ứng dụng chạy nhất quán bất kể môi trường triển khai là gì (server cá nhân, cloud VM, Kubernetes...).
Tại sao chọn Docker?
- Nhất quán môi trường: Loại bỏ vấn đề "nó chạy được trên máy tôi".
- Cô lập: Container chạy độc lập, không ảnh hưởng đến hệ thống host hoặc các container khác.
- Khả năng mở rộng: Dễ dàng scale ứng dụng bằng cách chạy nhiều instance của container.
- Quản lý đơn giản: Triển khai và quản lý ứng dụng qua các lệnh Docker hoặc nền tảng điều phối (Orchestration) như Kubernetes.
Các bước cơ bản:
Viết Dockerfile: File này chứa các chỉ dẫn để xây dựng (build) image Docker cho ứng dụng của bạn.
# Dockerfile # Giai đoạn Build: Sử dụng image Node.js để build ứng dụng FROM node:18-alpine AS builder WORKDIR /app # Copy package.json và lock file để tận dụng cache Docker layer COPY package.json ./ COPY yarn.lock ./ # hoặc package-lock.json nếu dùng npm # Cài đặt dependencies RUN yarn install --frozen-lockfile --production=false # hoặc npm ci # Copy toàn bộ mã nguồn COPY . . # Build ứng dụng Next.js (bao gồm biên dịch TypeScript) RUN yarn build # hoặc npm run build # Giai đoạn Run: Sử dụng image Node.js nhỏ hơn để chạy ứng dụng đã build FROM node:18-alpine AS runner WORKDIR /app # Copy file package.json (chỉ cần script start) và build output từ giai đoạn builder COPY --from=builder /app/package.json ./ COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public COPY --from=builder /app/node_modules ./node_modules # Copy dependencies production # Biến môi trường (nếu cần, có thể truyền vào lúc run container) # ENV NODE_ENV production # ENV PORT 3000 EXPOSE 3000 # Next.js production server chạy trên port 3000 mặc định # Lệnh để chạy ứng dụng khi container khởi động CMD ["yarn", "start"] # hoặc ["npm", "start"]
Giải thích: Dockerfile trên sử dụng Multi-stage build.
- Giai đoạn
builder
dùng image Node.js đầy đủ để cài đặt dependencies và chạynext build
. - Giai đoạn
runner
dùng một image Node.js nhỏ gọn hơn (alpine) chỉ để chạy output đã build. Chúng ta chỉ copy những gì cần thiết (.next
,public
,node_modules
production,package.json
). Điều này giúp image Docker cuối cùng có kích thước nhỏ hơn, an toàn hơn. WORKDIR /app
: Đặt thư mục làm việc bên trong container.COPY
: Sao chép file từ host vào container.--from=builder
chỉ định sao chép từ giai đoạnbuilder
.RUN
: Thực thi lệnh trong container.EXPOSE
: Thông báo container lắng nghe trên port nào (không thực sự publish port ra ngoài).CMD
: Lệnh mặc định chạy khi container khởi động.
- Giai đoạn
Build image Docker: Từ thư mục chứa Dockerfile và mã nguồn dự án, chạy lệnh build.
docker build -t my-next-app .
Giải thích: Lệnh này xây dựng image Docker dựa trên Dockerfile trong thư mục hiện tại (
.
).-t my-next-app
đặt tên cho image làmy-next-app
.Chạy container: Chạy một instance của image Docker đã build.
docker run -p 80:3000 my-next-app
Giải thích: Lệnh này tạo và chạy một container từ image
my-next-app
.-p 80:3000
ánh xạ port 80 trên host ra port 3000 bên trong container (nơi ứng dụng Next.js đang chạy).
Triển khai với Docker rất linh hoạt. Bạn có thể đẩy image lên một Docker Registry (như Docker Hub, AWS ECR, Google Container Registry) và triển khai trên các dịch vụ hỗ trợ container (ECS, EKS, GKE, Azure Kubernetes Service, DigitalOcean Kubernetes...).
TypeScript có ảnh hưởng gì đến Deployment?
Điểm mấu chốt cần nhớ là TypeScript không ảnh hưởng trực tiếp đến quy trình triển khai trên các nền tảng hosting như Vercel, Netlify, hay khi chạy bằng next start
.
Lý do là quá trình next build
đã bao gồm việc biên dịch (compiling) mã TypeScript sang JavaScript, kiểm tra kiểu, và tối ưu hóa. Kết quả cuối cùng của next build
là các file JavaScript, HTML, CSS, v.v., mà không còn mã TypeScript gốc nữa.
Các nền tảng deployment chỉ làm việc với output của next build
(thư mục .next
và public
). Họ không cần cài đặt TypeScript hay chạy trình biên dịch TypeScript (tsc
).
Lợi ích của TypeScript trong bối cảnh Deployment:
- Phát hiện lỗi sớm: Quá trình build sẽ thất bại nếu có lỗi kiểu trong mã TypeScript. Điều này giúp bạn bắt được nhiều bug trước khi deploy, thay vì phát hiện chúng ở môi trường production. Đây là một lợi thế đáng kể so với JavaScript thuần.
- Độ tin cậy cao hơn: Mã nguồn được kiểm tra kiểu giúp giảm khả năng xảy ra các lỗi runtime liên quan đến kiểu dữ liệu không mong muốn sau khi deploy.
Tóm lại, việc sử dụng TypeScript làm cho giai đoạn build an toàn và đáng tin cậy hơn, nhưng bản thân quy trình deployment (đặt file lên server và chạy) không khác biệt so với một ứng dụng Next.js viết bằng JavaScript thuần.
Những lưu ý quan trọng và Best Practices
- Biến môi trường: Luôn luôn sử dụng biến môi trường để lưu trữ thông tin cấu hình nhạy cảm (API keys, database credentials) hoặc các giá trị thay đổi giữa các môi trường (dev, staging, production). Không bao giờ commit các thông tin nhạy cảm trực tiếp vào mã nguồn hoặc file
.env
mà không đưa vào.gitignore
. - CI/CD (Continuous Integration/Continuous Deployment): Tự động hóa quy trình build, test và deploy mỗi khi có sự thay đổi mã nguồn. Các nền tảng như Vercel và Netlify có CI/CD tích hợp sẵn. Với self-hosting hoặc Docker, bạn có thể dùng GitHub Actions, GitLab CI, Jenkins, CircleCI...
- Tối ưu hóa Build: Thời gian build ảnh hưởng đến tốc độ của quy trình CI/CD. Đảm bảo bạn sử dụng cache dependency hiệu quả (ví dụ: trong Dockerfile hoặc cấu hình CI).
- Giám sát (Monitoring): Sau khi deploy, hãy thiết lập công cụ giám sát để theo dõi hiệu suất ứng dụng, lỗi runtime, và lưu lượng truy cập. Vercel và Netlify cung cấp một số tính năng monitoring cơ bản, hoặc bạn có thể tích hợp với các dịch vụ bên thứ ba như Sentry, Datadog, New Relic...
- Custom Domain và SSL: Hầu hết các nền tảng hosting (Vercel, Netlify) đều cung cấp cách dễ dàng để cấu hình tên miền tùy chỉnh và chứng chỉ SSL/HTTPS miễn phí (thường qua Let's Encrypt). Với self-hosting, bạn cần tự cấu hình SSL, thường là sử dụng Certbot để tích hợp Let's Encrypt với Nginx/Caddy.
- Cache: Hiểu cách Next.js xử lý cache (ví dụ: cache cho các trang SSG, cache cho API responses) và cách nền tảng deployment/reverse proxy của bạn xử lý caching (cache ở CDN, cache trình duyệt).
Comments