process.env là một đối tượng có một thuộc tính cho mỗi biến môi trường (environment variable) mà shell truyền cho chương trình Node.js của bạn: process.env.PORT, process.env.DATABASE_URL, v.v. Một biến chưa được đặt sẽ đọc ra undefined, còn biến đã được đặt thì luôn là chuỗi. Hãy đọc nó một lần lúc khởi động, cho nó một giá trị mặc định và chuyển sang kiểu dữ liệu bạn cần.
Kết quả in ra là shop on port 8080 in eu-west (REGION chưa bao giờ được đặt, nên giá trị mặc định được dùng), rồi string number: biến vẫn là văn bản cho đến khi Number() chuyển đổi nó. Xóa hai câu lệnh gán đầu tiên rồi chạy lại để thấy mọi giá trị mặc định.
Đặt biến môi trường trong shell
Các biến đến từ nơi Node.js được khởi chạy. Trong terminal:
// macOS and Linux: for one command
PORT=8080 NODE_ENV=production node app.js
// macOS and Linux: for the rest of the terminal session
export PORT=8080
// Windows PowerShell
$env:PORT = "8080"; node app.js
// Windows cmd.exe
set PORT=8080
node app.js
Các nền tảng hosting (Heroku, Render, Vercel, Docker, Kubernetes) có trang cài đặt hoặc tệp cấu hình cho việc này; code của bạn vẫn đọc chúng theo cùng một cách.
Mọi thứ đều là chuỗi
Đây là nguồn gốc của phần lớn bug liên quan tới process.env. 'false' và '0' là chuỗi không rỗng, nên cả hai đều là truthy:
Cách kiểm tra ngây thơ in ra debug is ON cho một biến được đặt là false, vì vậy hãy so sánh với đúng chuỗi bạn mong đợi. Với số, Number(process.env.X) biến một giá trị bị thiếu thành NaN, nên hãy áp giá trị mặc định trước khi chuyển đổi, như ví dụ đầu tiên đã làm với ??.
NODE_ENV
NODE_ENV là một quy ước, không phải tính năng của Node.js: Node.js không bao giờ tự đặt nó. Các thư viện mới là nơi đọc biến này. Chẳng hạn Express ẩn stack trace khỏi trang lỗi khi giá trị là production, và nhiều công cụ bỏ qua các bước kiểm tra chỉ dành cho môi trường phát triển.
Kết quả in ra là mode: production và verbose logs: false; đổi 'production' thành 'development' thì dòng thứ hai đổi theo. Chỉ nên dùng production, development và test, vì các thư viện kiểm tra chính xác những từ này.
Tệp .env: --env-file và dotenv
Gõ từng biến trước node mãi cũng chán. Tệp .env lưu chúng trong thư mục dự án, mỗi biến một dòng:
// .env
PORT=8080
DATABASE_URL=postgres://localhost/shop
API_KEY=dev-key-123
Node.js 20.6 trở lên đọc tệp này bằng một cờ, không cần package nào:
node --env-file=.env app.js
Với các phiên bản cũ hơn, hoặc nếu bạn thích, hãy cài package dotenv (npm install dotenv) và nạp nó ở ngay đầu tệp entry, trước khi có bất kỳ đoạn code nào đọc process.env:
require('dotenv').config(); // copies .env into process.env
console.log(process.env.PORT); // 8080
Cả hai cách đều không ghi đè biến đã được đặt sẵn trong shell, nên cấu hình production luôn được ưu tiên hơn nội dung tệp.
Dừng sớm khi thiếu biến
Thiếu URL cơ sở dữ liệu thì chương trình nên dừng ngay lúc khởi động với một thông báo rõ ràng, thay vì sập ở request đầu tiên một tiếng sau đó:
Kết quả in ra là Missing environment variables: API_KEY; thêm process.env.API_KEY = 'test'; ở đầu tệp thì nó in ra Config OK. Trong một chương trình thật, hãy gọi process.exit(1) ngay sau thông báo, vì mã thoát khác 0 là cách script triển khai hoặc nền tảng container biết rằng quá trình khởi động đã thất bại.
Đừng bao giờ commit thông tin bí mật
Đưa .env vào .gitignore ngay từ ngày bạn tạo nó. Thay vào đó, hãy commit một tệp .env.example chứa tên biến và giá trị giả, để người khác biết cần đặt những gì. Một key từng bị push lên repository phải được coi là đã lộ và cần thay mới, kể cả khi sau đó bạn xóa commit.
Câu hỏi thường gặp
Vì sao process.env.MY_VAR là undefined?
Biến đó chưa được đặt trong shell đã khởi chạy Node.js, hoặc được đặt ở một terminal khác. Tệp .env cũng không tự động được đọc: hãy chạy Node.js với --env-file=.env hoặc nạp nó bằng dotenv trước khi đọc giá trị.
Có thể thay đổi process.env khi chương trình đang chạy không?
Có. Gán process.env.X = 'value' sẽ thay đổi biến cho tiến trình hiện tại và cho mọi tiến trình con được khởi chạy sau đó, nhưng không bao giờ ảnh hưởng tới shell đã chạy Node.js. Node.js chuyển giá trị bạn gán thành chuỗi.
Tên biến môi trường có phân biệt chữ hoa chữ thường không?
Trên Linux và macOS thì có: PORT và port là hai biến khác nhau. Trên Windows thì không. Cứ dùng tên viết hoa với dấu gạch dưới là bạn sẽ không bao giờ phải bận tâm chuyện này.
Trình duyệt có đọc được process.env không?
Không. Trình duyệt không có biến môi trường. Các công cụ build như Vite và Next.js sao chép một số biến được chọn vào code frontend lúc build, điều đó cũng có nghĩa là các giá trị đó ai cũng xem được.