Config adalah nilai yang beda antara laptopmu dan server: port, alamat database, secret untuk JWT, mode aplikasi. Nilai-nilai itu gak ditulis di kode, tapi diberikan dari luar lewat environment variable: pasangan NAMA=nilai yang diterima program saat dijalankan. Bayangin rumah makan yang punya banyak cabang. Buku resepnya sama dan difotokopi ke semua cabang. Alamat cabang, nomor telepon, dan kunci gudang ditulis di kartu yang ditempel di dapur masing-masing, bukan di buku resep. Buku resep itu kodemu di git, kartunya adalah environment tiap mesin, dan kunci gudang adalah secret yang gak boleh ikut difotokopi.
🧪 Tebakan cepat:
.envberisiPORT=3000, dan kodenyaconst port = process.env.PORT + 1;. Apa isiport? Tebak dulu, jawabannya ada di bagian "Nilainya selalu teks".
process.env: nilai dari luar kode
Di Node, semua environment variable ada di object process.env. Di app.js milik catatan-api:
const port = Number(process.env.PORT) || 3000;
app.listen(port, () => {
console.log(`Server jalan di http://localhost:${port}`);
});
$ node app.js
Server jalan di http://localhost:3000
$ PORT=8080 node app.js
Server jalan di http://localhost:8080
process.env.PORTmembaca variabelPORT. Kalau gak diset, hasilnyaundefined, dan|| 3000memakai 3000.PORT=8080 node app.jsmengisiPORTuntuk satu kali jalan saja (bash dan zsh di Linux, macOS, Git Bash). Di PowerShell:$env:PORT=8080; node app.js.- Kodenya sama, hasilnya beda. Itu intinya: yang berubah antar mesin ada di environment.
Nilainya selalu teks
Isi process.env selalu string, sekalipun kelihatan seperti angka atau boolean:
| Kode | Hasil |
|---|---|
process.env.PORT |
"3000" |
process.env.PORT + 1 |
"30001" |
Number(process.env.PORT) + 1 |
3001 |
process.env.DEBUG dengan DEBUG=false |
"false", dan di if dianggap true |
Itu jawaban tebakannya: "30001". Tanda + menyambung teks, bukan menjumlah. Ubah dulu dengan Number(...), dan bandingkan boolean secara eksplisit: process.env.DEBUG === "true".
File .env dan tiga cara memuatnya
Mengetik semua variabel di depan node tiap kali itu repot. Saat develop, nilainya ditaruh di file .env di root proyek:
# .env catatan-api (jangan di-commit)
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://budi:rahasia@localhost:5432/catatan
Node gak membaca file ini sendiri. Ada tiga cara memuatnya ke process.env:
node --env-file=.env app.js: bawaan Node 20.6 ke atas, tanpa install apa-apa.process.loadEnvFile();di baris paling atas: bawaan Node 20.12 ke atas, membaca.envdi folder tempat kamu menjalankannode.- Package dotenv:
npm install dotenv, lalurequire("dotenv").config();di baris paling atas. Ini cara klasik yang jalan di versi Node mana pun.
Ketiganya memakai format yang sama: satu NAMA=nilai per baris, # untuk komentar, tanda kutip boleh untuk nilai yang ada spasinya.
Siapa yang menang: shell atau .env
Variabel yang sudah ada di environment menang atas isi file, baik dengan --env-file maupun dotenv:
$ PORT=8080 node --env-file=.env app.js
Server jalan di http://localhost:8080
.env bilang 3000, shell bilang 8080, dan yang dipakai 8080. Ini sengaja: di server, nilai yang diset hosting atau systemd gak tertimpa file yang kebetulan ikut ter-upload.
NODE_ENV: development atau production
NODE_ENV adalah nama yang disepakati bersama untuk mode aplikasi:
- Dengan
NODE_ENV=production, halaman error bawaan Express gak menampilkan stack trace (pelajaran Error Handling), dan template di-cache. npm installdannpm cidenganNODE_ENV=productionmelewatkandevDependencies, misalnya alat test dari pelajaran Testing API.- Di kodemu:
const isProduction = process.env.NODE_ENV === "production";. Tulis persisproduction, karena library cuma mengecek kata itu, bukanprod.
Satu file config, dicek saat start
Baca semua variabel di satu file, dan tolak start kalau ada yang wajib tapi kosong:
// config.js
const wajib = ["DATABASE_URL", "JWT_SECRET"];
const kurang = wajib.filter((nama) => !process.env[nama]);
if (kurang.length > 0) {
console.error(`Config error: ${kurang.join(", ")} belum diset. Salin .env.example jadi .env, lalu isi.`);
process.exit(1);
}
module.exports = {
env: process.env.NODE_ENV || "development",
port: Number(process.env.PORT) || 3000,
databaseUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
};
Dengan .env di atas (belum ada JWT_SECRET):
$ node --env-file=.env app.js
Config error: JWT_SECRET belum diset. Salin .env.example jadi .env, lalu isi.
$ echo $?
1
wajib.filter((nama) => !process.env[nama])mengumpulkan semua nama yang kosong, bukan berhenti di yang pertama.process.exit(1)menghentikan program dengan kode 1, tanda gagal. Ini namanya fail fast: gagal di detik pertama dengan pesan yang jelas lebih baik daripada server menyala lalu error aneh saat login pertama, jam 2 pagi.- File lain cukup
const config = require("./config");lalu memakaiconfig.port, tanpa menyentuhprocess.envlagi. - Untuk aturan yang lebih banyak (harus angka, harus URL), library seperti
zodatauenvalidmemeriksa dengan skema.
Secret gak pernah masuk git
.envmasuk.gitignore. Yang di-commit adalah.env.example: nama-namanya saja, tanpa nilai, supaya teman setim tahu apa yang harus diisi.- Di server, nilainya diset di dashboard hosting, di unit systemd, atau di PM2 (pelajaran Deploy Node.js berikutnya).
const config = { dbPassword: "rahasia123" }di kode berarti secret-nya tersimpan di git history, di setiap clone. Apa yang harus dilakukan kalau sudah bocor ada di pelajaran Environment Variables & Secrets di track Security.
# .env.example (di-commit)
NODE_ENV=development
PORT=3000
DATABASE_URL=
JWT_SECRET=
Di latihan: config per environment
Kode awal sudah mengisi process.env.PORT = "3000" dan object config dengan development (port 3000, logLevel: "debug") dan production (port 8080, logLevel: "warn"). Kamu menulis getConfig(env) yang mengembalikan config[env] || config.development, dan validateEnv(required) yang memakai filter dengan !process.env[key], lalu mengembalikan { valid: missing.length === 0, missing }. Di simulator latihan, DATABASE_URL belum diset, jadi baris terakhirnya Env check: {"valid":false,"missing":["DATABASE_URL"]}.
⚠️ Jebakan yang sering ketemu
- Lupa nilainya teks.
process.env.PORT + 1jadi"30001", danDEBUG=falsetetap lolosif. - Memuat
.envterlambat.require("dotenv").config()yang ditaruh setelahrequire("./config")membuat config membacaundefined. Muat paling atas. - Salah ketik nama.
process.env.PROT || 3000diam-diam memberi 3000. Daftarwajibyang dicek saat start menangkap salah ketik pada variabel wajib. - Secret di kode atau
.envter-commit. Sekali masuk git, anggap bocor.
TL;DR: Nilai yang beda antar mesin (port, database, secret, mode) dibaca dari process.env, bukan ditulis di kode. Isinya selalu string: process.env.PORT + 1 memberi "30001", jadi pakai Number(...). File .env dimuat dengan node --env-file=.env app.js (Node 20.6+), process.loadEnvFile(), atau package dotenv, dan variabel yang sudah ada di environment menang atas file. NODE_ENV=production mengubah perilaku Express dan npm. Baca config di satu file, cek yang wajib saat start, lalu process.exit(1) dengan pesan jelas (fail fast). .env di .gitignore, .env.example yang di-commit, dan secret gak pernah ada di kode.