REST API adalah tulang punggung hampir semua aplikasi web dan mobile modern. Sebelumnya kita sudah membahas konsep dasar REST API serta cara membangunnya dengan FastAPI di Python. Kali ini, kita akan melakukannya dengan stack paling populer di sisi server JavaScript: Node.js dan Express.
Express adalah framework web minimalis untuk Node.js yang menjadi standar de facto dalam membangun API berkat kesederhanaan dan ekosistemnya yang luas. Dalam tutorial ini kamu akan membangun API "Todo List" lengkap dengan operasi CRUD, middleware, validasi data, dan penanganan error — semuanya dari nol dengan kode yang bisa langsung kamu jalankan.
Apa yang Akan Kamu Bangun
Di akhir tutorial ini, kamu akan memiliki REST API untuk mengelola daftar tugas (todo). Berikut endpoint-endpoint yang akan kita buat — coba jalankan perintah curl ini setelah server berjalan untuk menguji setiap endpoint:
# Ambil semua todo
curl http://localhost:3000/todos
# Tambah todo baru
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Belajar Express"}'
# Perbarui todo (id 1)
curl -X PUT http://localhost:3000/todos/1 -H "Content-Type: application/json" -d '{"done": true}'
# Hapus todo (id 1)
curl -X DELETE http://localhost:3000/todos/1
Prasyarat
Untuk mengikuti tutorial ini, pastikan kamu sudah memiliki Node.js versi 18 ke atas (cek dengan node -v) dan npm yang biasanya ikut terinstal bersamanya. Kamu juga perlu memahami dasar JavaScript seperti variabel, fungsi, dan array. Jika masih baru dengan JavaScript, kamu bisa mulai dari panduan dasar JavaScript kami.
1. Menyiapkan Proyek
Buat folder baru untuk proyek, lalu inisialisasi dengan npm dan instal Express:
mkdir express-todo-api
cd express-todo-api
npm init -y
npm install express
Perintah npm init -y membuat file package.json dengan konfigurasi bawaan, sedangkan npm install express menambahkan Express ke folder node_modules dan mencatatnya sebagai dependensi proyek.
2. Membuat Server Dasar
Buat file server.js dan tulis kode server paling sederhana berikut:
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.json({ message: "Halo dari Express!" });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server berjalan di http://localhost:${PORT}`);
});
Jalankan dengan node server.js, lalu buka http://localhost:3000 di browser atau jalankan curl http://localhost:3000. Kamu akan melihat respons JSON. process.env.PORT memungkinkan port diatur lewat environment variable, yang berguna saat deploy.
Tips: Daripada mengulang perintah node server.js setiap kali mengubah kode, gunakan mode watch bawaan Node.js 18+ dengan node --watch server.js, atau pasang nodemon dengan npm install -D nodemon. Server akan otomatis restart setiap kali kamu menyimpan file.
3. Membangun Endpoint CRUD
Sekarang kita bangun operasi CRUD (Create, Read, Update, Delete) untuk todo. Untuk tutorial ini kita simpan data di variabel array dalam memori agar fokus pada konsep API-nya — data akan hilang saat server dimatikan, dan kita bahas cara menyambungkan database di bagian akhir.
const express = require("express");
const app = express();
app.use(express.json());
// "Database" sementara di memori
let todos = [
{ id: 1, title: "Belajar Node.js", done: false },
{ id: 2, title: "Buat REST API", done: false },
];
// READ: ambil semua todo
app.get("/todos", (req, res) => {
res.json(todos);
});
// READ: ambil satu todo berdasarkan id
app.get("/todos/:id", (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: "Todo tidak ditemukan" });
}
res.json(todo);
});
// CREATE: tambah todo baru
app.post("/todos", (req, res) => {
const { title } = req.body;
if (!title || title.trim() === "") {
return res.status(400).json({ error: "Field 'title' wajib diisi" });
}
const newTodo = {
id: todos.length ? todos[todos.length - 1].id + 1 : 1,
title,
done: false,
};
todos.push(newTodo);
res.status(201).json(newTodo);
});
// UPDATE: perbarui todo berdasarkan id
app.put("/todos/:id", (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: "Todo tidak ditemukan" });
}
const { title, done } = req.body;
if (title !== undefined) todo.title = title;
if (done !== undefined) todo.done = done;
res.json(todo);
});
// DELETE: hapus todo berdasarkan id
app.delete("/todos/:id", (req, res) => {
const index = todos.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: "Todo tidak ditemukan" });
}
const [removed] = todos.splice(index, 1);
res.json(removed);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server berjalan di http://localhost:${PORT}`);
});
Beberapa hal penting dari kode di atas. app.use(express.json()) adalah middleware yang mengurai body request berformat JSON ke req.body. Route parameter seperti :id dibaca lewat req.params.id — ingat untuk mengonversinya dengan Number() karena nilainya selalu berupa string. Status code juga penting: 201 untuk resource yang berhasil dibuat, 404 saat data tidak ditemukan, dan 400 untuk input yang tidak valid.
4. Menambahkan Middleware Logger
Middleware adalah fungsi yang berjalan di antara request masuk dan handler meresponsnya. Salah satu kegunaan paling umum adalah logging, untuk melihat aktivitas yang terjadi di API-mu saat pengembangan:
// Middleware logger: jalankan untuk SETIAP request
app.use((req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next(); // lanjutkan ke middleware/handler berikutnya
});
Letakkan blok ini sebelum definisi route agar semua request tercatat. Perhatikan parameter next — tanpa memanggilnya, request akan menggantung dan tidak pernah mencapai handler route.
5. Validasi Data
Jangan pernah percaya input dari client. Buat fungsi validasi kecil agar API-mu konsisten dan mudah dirawat:
function validateTodo(payload) {
const errors = [];
if (payload.title === undefined || payload.title.trim() === "") {
errors.push("Field 'title' wajib diisi");
} else if (payload.title.length > 100) {
errors.push("Field 'title' maksimal 100 karakter");
}
if (payload.done !== undefined && typeof payload.done !== "boolean") {
errors.push("Field 'done' harus bertipe boolean");
}
return errors;
}
Gunakan fungsi ini di endpoint POST dan PUT. Jika ada error, kembalikan status 400 beserta daftar errornya:
app.post("/todos", (req, res) => {
const errors = validateTodo(req.body);
if (errors.length > 0) {
return res.status(400).json({ errors });
}
const newTodo = {
id: todos.length ? todos[todos.length - 1].id + 1 : 1,
title: req.body.title,
done: req.body.done ?? false,
};
todos.push(newTodo);
res.status(201).json(newTodo);
});
6. Penanganan Error dan 404
Terakhir, tangani endpoint yang tidak dikenali serta error tak terduga dengan dua middleware khusus. Middleware error di Express wajib memiliki empat parameter agar dikenali sebagai error handler:
// 404: endpoint tidak ditemukan (letakkan SETELAH semua route)
app.use((req, res) => {
res.status(404).json({ error: "Endpoint tidak ditemukan" });
});
// Error handler: tangkap error tak terduga (harus 4 parameter)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: "Terjadi kesalahan di server" });
});
Urutan sangat penting di sini. Middleware 404 diletakkan setelah semua route, sehingga hanya terpanggil jika tidak ada route yang cocok. Error handler meneruskan error dari middleware lain (misalnya lewat next(err)) dan mengembalikan respons 500 yang aman tanpa membocorkan detail internal.
Langkah berikutnya: Untuk aplikasi nyata, ganti array dalam memori dengan database. Kamu bisa menggunakan MongoDB dengan Mongoose, atau PostgreSQL/MySQL dengan library seperti pg dan mysql2. Konsep routing dan middleware yang kamu pelajari di sini tetap sama persis — hanya lapisan penyimpanannya yang berubah.
Kesimpulan
Selamat! Kamu baru saja membangun REST API lengkap dengan Node.js dan Express. Kamu sudah memahami cara menyiapkan proyek, membuat route CRUD, memakai middleware, memvalidasi input, dan menangani error dengan benar. Konsep-konsep ini menjadi fondasi yang sama untuk hampir semua API Node.js di dunia nyata. Jika ingin membandingkan dengan pendekatan Python, lihat kembali tutorial REST API dengan FastAPI kami, atau perdalam pemahaman JavaScript-mu lewat dasar JavaScript.