Kenapa ini penting
Express adalah framework web Node yang paling banyak dipakai. Ia sudah ada sejak 2010, dokumentasinya lengkap, dan hampir setiap tutorial, library autentikasi, atau middleware pihak ketiga mendukungnya. Setelah lama tertahan di versi 4, Express 5 akhirnya stabil dan sekarang menjadi versi default di npm (npm view express version → 5.3.0 per 24 Oktober 2026). Versi 5 membawa perubahan penting: error dari fungsi async otomatis ditangkap, sintaks path lebih ketat, dan beberapa API lama dihapus.
Banyak tutorial di internet masih menulis kode Express 4. Sebagian besar tetap jalan, tapi ada beberapa yang langsung error di Express 5 (misalnya rute "/*"). Di modul ini kamu belajar dasar Express dengan cara versi 5.
Konsep inti
Instalasi
mkdir belajar-express && cd belajar-express
npm init -y
npm pkg set type=module # aktifkan ESM
npm install express # memasang express@5.3.0
Untuk TypeScript, pasang juga npm install -D @types/express, lalu tipe handler bisa ditulis (req: Request, res: Response) => ... dari import type { Request, Response } from "express".
Aplikasi dan rute
import express from "express";
const app = express();
app.get("/", (req, res) => res.send("Halo"));
app.listen(3000);
Pola rute: app.METHOD(path, handler), dengan METHOD berupa get, post, put, patch, delete, atau all. Express mencocokkan rute dari atas ke bawah; rute pertama yang cocok dan mengirim response akan menang.
Objek req di Express
Express menambahkan banyak kemudahan di atas req bawaan Node:
| Properti | Contoh request | Nilai |
|---|---|---|
req.params |
GET /produk/3 di rute /produk/:id |
{ id: "3" } (selalu string) |
req.query |
/produk?kategori=minuman&maks=16000 |
{ kategori: "minuman", maks: "16000" } |
req.body |
POST JSON | objek hasil parse, atau undefined kalau tidak ada parser/body |
req.get("user-agent") |
header | nilai header (tidak peka huruf besar/kecil) |
req.path, req.method, req.ip |
path tanpa query, method, IP client |
Tiga hal khas Express 5 yang perlu kamu ingat:
req.bodybernilaiundefinedkalau body tidak di-parse (di Express 4 nilainya{}). Jadiconst { nama } = req.bodybisa error. Pakaireq.body ?? {}.req.queryhanya getter. Kamu bisa membacanya, tapi tidak bisa menimpa (req.query = ...) seperti di Express 4.- Query parser default adalah
"simple", berbasisquerystringbawaan Node. Nilai query selalu string, atau array string kalau kunci yang sama muncul dua kali (?kategori=a&kategori=b). Notasi objek bersarang seperti?filter[harga]=10tidak diurai kecuali kamu mengaturapp.set("query parser", "extended").
Body JSON
Express tidak membaca body secara otomatis. Pasang middleware app.use(express.json()) di awal. Middleware ini hanya mem-parse request dengan header Content-Type: application/json. Kalau JSON-nya rusak, ia meneruskan error berstatus 400 ke error handler.
Objek res di Express
| Metode | Fungsi |
|---|---|
res.send(data) |
Kirim string/HTML/Buffer, Content-Type ditebak |
res.json(data) |
Kirim JSON dengan header yang benar |
res.status(kode) |
Set status, bisa dirantai: res.status(201).json(...) |
res.sendStatus(kode) |
Kirim status dengan teks standar |
res.set(nama, nilai) / res.location(url) |
Set header |
res.redirect(url) |
Redirect (default 302) |
res.end() |
Akhiri tanpa body (misalnya untuk 204) |
Di Express 5, res.status() hanya menerima bilangan bulat 100 sampai 999; kode tidak valid langsung melempar error.
Status code yang paling sering dipakai
| Kode | Arti | Kapan |
|---|---|---|
| 200 OK | Berhasil | GET/PATCH sukses |
| 201 Created | Data baru dibuat | POST sukses |
| 204 No Content | Berhasil, tanpa body | DELETE sukses |
| 400 Bad Request | Request rusak | JSON tidak valid, input salah |
| 401 Unauthorized | Belum login / kredensial salah | Token tidak ada |
| 403 Forbidden | Sudah login tapi tidak berhak | Bukan admin |
| 404 Not Found | Tidak ada | Rute atau data tidak ditemukan |
| 500 Internal Server Error | Bug di server | Error tak terduga |
Contoh kode
// express-dasar.js, jalankan: node express-dasar.js
import express from "express";
const app = express();
app.use(express.json()); // parse body JSON
const produk = [
{ id: 1, nama: "Kopi Susu", harga: 18000, kategori: "minuman" },
{ id: 2, nama: "Teh Tarik", harga: 15000, kategori: "minuman" },
{ id: 3, nama: "Roti Bakar", harga: 22000, kategori: "makanan" },
];
app.get("/", (req, res) => {
res.send("Halo dari Express 5!");
});
// Query: /produk?kategori=minuman&maks=16000
app.get("/produk", (req, res) => {
const { kategori, maks } = req.query; // semuanya string
let hasil = produk;
if (kategori) hasil = hasil.filter((p) => p.kategori === kategori);
if (maks) hasil = hasil.filter((p) => p.harga <= Number(maks));
res.json(hasil);
});
// Params: /produk/3
app.get("/produk/:id", (req, res) => {
const item = produk.find((p) => p.id === Number(req.params.id)); // params juga string
if (!item) return res.status(404).json({ error: "Produk tidak ditemukan" });
res.json(item);
});
// Body JSON
app.post("/produk", (req, res) => {
console.log("body:", req.body);
const { nama, harga, kategori } = req.body ?? {}; // Express 5: body bisa undefined
if (!nama || typeof harga !== "number") {
return res.status(400).json({ error: "nama dan harga (angka) wajib diisi" });
}
const baru = { id: produk.length + 1, nama, harga, kategori: kategori ?? "lainnya" };
produk.push(baru);
res.status(201).json(baru);
});
app.get("/info", (req, res) => {
res.json({ method: req.method, path: req.path, ip: req.ip, ua: req.get("user-agent") });
});
const PORT = process.env.PORT ?? 3000;
app.listen(PORT, (err) => {
if (err) throw err; // Express 5: error listen (misalnya EADDRINUSE) dikirim ke callback
console.log(`Server Express jalan di http://localhost:${PORT}`);
});
Hasil uji dengan curl (flag -w ' [%{http_code}]' menampilkan status code di akhir):
curl -s localhost:3000/
# → Halo dari Express 5!
curl -s "localhost:3000/produk?kategori=minuman&maks=16000"
# → [{"id":2,"nama":"Teh Tarik","harga":15000,"kategori":"minuman"}]
curl -s localhost:3000/produk/3
# → {"id":3,"nama":"Roti Bakar","harga":22000,"kategori":"makanan"}
curl -s -w ' [%{http_code}]' localhost:3000/produk/99
# → {"error":"Produk tidak ditemukan"} [404]
curl -s -w ' [%{http_code}]' -X POST localhost:3000/produk \
-H 'Content-Type: application/json' \
-d '{"nama":"Es Jeruk","harga":12000,"kategori":"minuman"}'
# → {"id":4,"nama":"Es Jeruk","harga":12000,"kategori":"minuman"} [201]
# Tanpa header JSON (curl -d default-nya form urlencoded):
curl -s -w ' [%{http_code}]' -X POST localhost:3000/produk -d 'nama=x'
# → {"error":"nama dan harga (angka) wajib diisi"} [400]
# Log server: body: undefined ← express.json() tidak mem-parse body non-JSON
curl -s localhost:3000/info -A "curl-belajar"
# → {"method":"GET","path":"/info","ip":"::1","ua":"curl-belajar"}
# Query dengan kunci ganda menjadi array, sehingga filter === gagal:
curl -s "localhost:3000/produk?kategori=minuman&kategori=makanan"
# → []
curl -s -w ' [%{http_code}]' localhost:3000/tidak-ada
# → <!DOCTYPE html> ... <pre>Cannot GET /tidak-ada</pre> ... [404]
Dua hal terakhir menunjukkan "sisi kasar" bawaan Express: rute yang tidak ada dibalas dengan HTML, dan JSON rusak juga dibalas HTML berisi stack trace. Untuk API, kita mau semua jawaban berupa JSON. Itu akan dibereskan di modul Routing & Middleware dan Validasi & Error Handling.
Kesalahan umum
- Lupa
app.use(express.json()), atau mengirim request tanpa headerContent-Type: application/json. Hasilnyareq.bodybernilaiundefined. - Membandingkan
req.params.iddengan angka."3" === 3bernilaifalse. Ubah denganNumber(), atau lebih baik validasi dengan Zod. - Menganggap
req.query.xselalu string tunggal. Bisa juga array (kunci ganda) atauundefined. Validasi bentuknya. - Lupa
returnsetelahres.status(404).json(...). Kode di bawahnya tetap jalan dan mencoba mengirim response kedua (ERR_HTTP_HEADERS_SENT). - Mengirim status 200 untuk semua hal, termasuk error, dengan body
{ "sukses": false }. Client, cache, dan alat monitoring mengandalkan status code. Pakai kode yang benar.
Latihan
- Mudah, Tambahkan rute
GET /produk/murahyang mengembalikan produk dengan harga di bawah 16000. Di mana kamu harus meletakkannya relatif terhadap/produk/:id, dan kenapa? - Sedang, Tambahkan
DELETE /produk/:idyang membalas 204 kalau berhasil dan 404 kalau tidak ada. - Menantang, Tambahkan query
urut=hargaatauurut=-hargapadaGET /produk(minus berarti menurun). Nilai lain dibalas 400.
Lihat solusi
1. Letakkan sebelum /produk/:id. Kalau sesudahnya, request /produk/murah akan cocok dengan /:id (dengan id = "murah") dan dibalas 404.
app.get("/produk/murah", (req, res) => {
res.json(produk.filter((p) => p.harga < 16000));
});
app.get("/produk/:id", /* ... */);
2.
app.delete("/produk/:id", (req, res) => {
const index = produk.findIndex((p) => p.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: "Produk tidak ditemukan" });
produk.splice(index, 1);
res.status(204).end();
});
3.
app.get("/produk", (req, res) => {
const { urut } = req.query;
let hasil = [...produk];
if (urut !== undefined) {
if (urut !== "harga" && urut !== "-harga") {
return res.status(400).json({ error: "urut harus 'harga' atau '-harga'" });
}
const arah = urut === "-harga" ? -1 : 1;
hasil.sort((a, b) => (a.harga - b.harga) * arah);
}
res.json(hasil);
});
Ringkasan
- Express 5 (5.3.0) adalah versi default; pasang dengan
npm install expressdan pakai ESM. - Rute ditulis
app.METHOD(path, handler)dan dicocokkan dari atas ke bawah. req.paramsdanreq.queryberisi string;req.querydi Express 5 hanya getter dan memakai parser"simple".express.json()wajib untuk membaca body JSON; tanpa body yang di-parse,req.bodybernilaiundefineddi Express 5.- Pakai
res.status(kode).json(data)dengan status code yang tepat, dan selalureturnsetelah mengirim response di tengah handler.