REST (Representational State Transfer) adalah arsitektur paling populer untuk membangun API web. Dengan Express.js, kamu bisa membangun RESTful API yang bersih dan terstruktur dengan sangat efisien.
Di artikel ini kita akan membangun API CRUD (Create, Read, Update, Delete) lengkap untuk resource “Artikel” — dari struktur project hingga semua endpoint.
Prinsip RESTful API
REST API mengikuti beberapa konvensi penting:
| HTTP Method | Endpoint | Aksi |
|---|---|---|
| GET | /artikel |
Ambil semua artikel |
| GET | /artikel/:id |
Ambil satu artikel |
| POST | /artikel |
Buat artikel baru |
| PUT | /artikel/:id |
Update artikel (replace) |
| PATCH | /artikel/:id |
Update artikel (partial) |
| DELETE | /artikel/:id |
Hapus artikel |
Konvensi lain yang perlu diikuti:
- URL menggunakan kata benda (noun), bukan kata kerja —
/artikelbukan/ambilArtikel - URL menggunakan huruf kecil dan tanda hubung
- Response menggunakan HTTP status code yang tepat
- Response body dalam format JSON
Setup Project
mkdir api-artikel
cd api-artikel
npm init -y
npm install express
Struktur folder:
api-artikel/
├── data/
│ └── artikel.js # "database" in-memory
├── routes/
│ └── artikel.js # route handlers
├── middleware/
│ └── validasi.js # validasi input
└── index.js
Data Layer (Simulasi Database)
data/artikel.js — data in-memory sebagai pengganti database:
let artikel = [
{
id: 1,
judul: 'Pengenalan Express.js',
konten: 'Express adalah framework minimalis untuk Node.js...',
kategori: 'tutorial',
penulis: 'Budi',
dibuatPada: new Date('2026-08-19').toISOString()
},
{
id: 2,
judul: 'Routing di Express',
konten: 'Routing menentukan bagaimana aplikasi merespons request...',
kategori: 'tutorial',
penulis: 'Sari',
dibuatPada: new Date('2026-08-20').toISOString()
}
];
let idCounter = artikel.length + 1;
module.exports = {
getAll: () => artikel,
getById: (id) => artikel.find(a => a.id === id),
create: (data) => {
const baru = { id: idCounter++, ...data, dibuatPada: new Date().toISOString() };
artikel.push(baru);
return baru;
},
update: (id, data) => {
const index = artikel.findIndex(a => a.id === id);
if (index === -1) return null;
artikel[index] = { ...artikel[index], ...data };
return artikel[index];
},
delete: (id) => {
const index = artikel.findIndex(a => a.id === id);
if (index === -1) return false;
artikel.splice(index, 1);
return true;
}
};
Route Handlers
routes/artikel.js:
const express = require('express');
const router = express.Router();
const db = require('../data/artikel');
// GET /artikel — ambil semua dengan pagination & filter
router.get('/', (req, res) => {
let data = db.getAll();
// Filter berdasarkan kategori
if (req.query.kategori) {
data = data.filter(a => a.kategori === req.query.kategori);
}
// Pencarian judul
if (req.query.q) {
const keyword = req.query.q.toLowerCase();
data = data.filter(a => a.judul.toLowerCase().includes(keyword));
}
// Pagination
const halaman = parseInt(req.query.halaman) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (halaman - 1) * limit;
const paginasi = data.slice(offset, offset + limit);
res.json({
total: data.length,
halaman,
limit,
data: paginasi
});
});
// GET /artikel/:id — ambil satu artikel
router.get('/:id', (req, res) => {
const id = parseInt(req.params.id);
const item = db.getById(id);
if (!item) {
return res.status(404).json({ error: 'Artikel tidak ditemukan' });
}
res.json(item);
});
// POST /artikel — buat artikel baru
router.post('/', (req, res) => {
const { judul, konten, kategori, penulis } = req.body;
if (!judul || !konten) {
return res.status(422).json({ error: 'Judul dan konten wajib diisi' });
}
const baru = db.create({ judul, konten, kategori: kategori || 'umum', penulis: penulis || 'Anonim' });
res.status(201).json(baru);
});
// PUT /artikel/:id — update penuh
router.put('/:id', (req, res) => {
const id = parseInt(req.params.id);
const { judul, konten, kategori, penulis } = req.body;
if (!judul || !konten) {
return res.status(422).json({ error: 'Judul dan konten wajib diisi' });
}
const diupdate = db.update(id, { judul, konten, kategori, penulis });
if (!diupdate) return res.status(404).json({ error: 'Artikel tidak ditemukan' });
res.json(diupdate);
});
// PATCH /artikel/:id — update sebagian
router.patch('/:id', (req, res) => {
const id = parseInt(req.params.id);
const diupdate = db.update(id, req.body);
if (!diupdate) return res.status(404).json({ error: 'Artikel tidak ditemukan' });
res.json(diupdate);
});
// DELETE /artikel/:id — hapus artikel
router.delete('/:id', (req, res) => {
const id = parseInt(req.params.id);
const berhasil = db.delete(id);
if (!berhasil) return res.status(404).json({ error: 'Artikel tidak ditemukan' });
res.status(204).send();
});
module.exports = router;
Entry Point
index.js:
const express = require('express');
const app = express();
app.use(express.json());
// Routes
const artikelRouter = require('./routes/artikel');
app.use('/api/artikel', artikelRouter);
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Endpoint tidak ditemukan' });
});
// Error handler
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Terjadi kesalahan server' });
});
app.listen(3000, () => {
console.log('API berjalan di http://localhost:3000');
});
Test API dengan curl
# Ambil semua artikel
curl http://localhost:3000/api/artikel
# Ambil dengan filter
curl "http://localhost:3000/api/artikel?kategori=tutorial&halaman=1"
# Buat artikel baru
curl -X POST http://localhost:3000/api/artikel \
-H "Content-Type: application/json" \
-d '{"judul":"Artikel Baru","konten":"Isi artikel...","kategori":"tutorial"}'
# Update artikel
curl -X PATCH http://localhost:3000/api/artikel/1 \
-H "Content-Type: application/json" \
-d '{"judul":"Judul Diperbarui"}'
# Hapus artikel
curl -X DELETE http://localhost:3000/api/artikel/1
HTTP Status Code yang Tepat
| Situasi | Status Code |
|---|---|
| Berhasil mengambil data | 200 OK |
| Berhasil membuat resource | 201 Created |
| Berhasil hapus (no content) | 204 No Content |
| Input tidak valid | 422 Unprocessable Entity |
| Tidak ditemukan | 404 Not Found |
| Tidak terautentikasi | 401 Unauthorized |
| Tidak diizinkan | 403 Forbidden |
| Kesalahan server | 500 Internal Server Error |
Kesimpulan
Dengan Express.js, membangun RESTful API yang bersih dan terstruktur sangat mudah. Kunci utamanya adalah mengikuti konvensi REST — URL sebagai noun, HTTP method sesuai aksi, dan status code yang tepat.
Di artikel berikutnya, kita hubungkan API ini ke database nyata menggunakan Prisma ORM agar data tersimpan secara persisten.
Kiki/🎮🍉⌨️🍩💻