Membuat REST API Sederhana dengan JavaScript Murni
Halo, Gabuters!
Pada artikel sebelumnya, kita sudah berhasil membuat HTTP server menggunakan JavaScript tanpa framework.
Server tersebut dapat menerima request, membaca URL, dan mengirim respons JSON.
Namun, sejauh ini kita baru menggunakan method GET untuk mengambil data.
Bagaimana jika kita ingin menambahkan artikel baru?
Bagaimana cara mengubah artikel yang sudah ada?
Bagaimana cara menghapus artikel?
Untuk menjawab pertanyaan tersebut, sekarang kita akan membuat REST API sederhana dengan operasi CRUD.
Menariknya, kita tetap tidak membutuhkan Express.
Kita akan menggunakan JavaScript, Node.js, dan modul bawaan node:http.
Di akhir artikel, kita akan mempunyai API artikel dengan fitur:
- Mengambil seluruh artikel.
- Mengambil detail artikel berdasarkan ID.
- Menambahkan artikel baru.
- Memperbarui artikel.
- Menghapus artikel.
- Memvalidasi request dan mengembalikan status code yang sesuai.
Data akan disimpan sementara di memori server.
Jadi, kita belum membutuhkan database.
Mari kita mulai!
1. Mengingat Kembali Konsep CRUD
CRUD merupakan singkatan dari:
- Create: membuat data.
- Read: membaca data.
- Update: mengubah data.
- Delete: menghapus data.
Dalam API HTTP, kita sering menggunakan pemetaan berikut:
| Operasi | HTTP method | Endpoint |
|---|---|---|
| Create | POST | /api/artikel |
| Read semua | GET | /api/artikel |
| Read detail | GET | /api/artikel/1 |
| Update | PUT | /api/artikel/1 |
| Delete | DELETE | /api/artikel/1 |
Contoh tersebut menggunakan pendekatan endpoint berbasis resource.
Resource yang kita kelola adalah artikel.
Perlu diingat, penggunaan pola endpoint dan HTTP method ini merupakan praktik umum dalam desain REST API. REST sendiri memiliki prinsip arsitektural yang lebih luas.
Untuk latihan, kita akan fokus pada implementasi API HTTP sederhana dengan pola tersebut.
2. Menyiapkan Proyek
Buat folder baru:
belajar-rest-api/Di dalamnya, buat file:
server.jsStrukturnya:
belajar-rest-api/└── server.jsKita tidak perlu menginstal package tambahan.
Pastikan Node.js sudah terpasang dengan menjalankan:
node --versionKemudian buka folder proyek menggunakan editor kode.
Seluruh kode pada artikel ini akan kita tulis dalam server.js.
3. Menyiapkan Data Artikel
Kita mulai dengan membuat array yang menyimpan data artikel.
let artikel = [ { id: 1, judul: "Belajar HTML", kategori: "HTML" }, { id: 2, judul: "Belajar CSS", kategori: "CSS" }, { id: 3, judul: "Belajar JavaScript", kategori: "JavaScript" }];
let idBerikutnya = 4;Variabel artikel menyimpan daftar artikel.
Sedangkan idBerikutnya digunakan untuk menghasilkan ID baru ketika kita menambahkan artikel.
Kita menggunakan let karena nilai variabel dapat berubah.
Misalnya, ketika artikel dihapus, kita akan memperbarui isi array.
Apakah ini sudah menggunakan database?
Belum.
Data hanya tersimpan di memori proses Node.js.
Jika server dihentikan dan dijalankan kembali, data akan kembali ke nilai awal yang tertulis dalam kode.
Penyimpanan permanen akan kita pelajari pada seri Database.
4. Membuat Function untuk Mengirim JSON
Ketika membuat API, kita akan sering mengirim respons JSON.
Daripada menulis header berulang kali, kita buat function pembantu.
function kirimJSON(res, statusCode, data) { res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
res.end(JSON.stringify(data));}Function ini menerima tiga parameter:
res: objek response.statusCode: status HTTP.data: data yang akan dikirim.
Contoh penggunaannya:
kirimJSON(res, 200, { pesan: "Request berhasil."});Function tersebut mengubah object menjadi string JSON dan mengirimkannya sebagai response.
Dengan begitu, kode routing menjadi lebih ringkas.
5. Membaca Request Body Tanpa Express
Ini salah satu bagian paling menarik.
Ketika client mengirim request POST atau PUT, data biasanya dikirim melalui request body.
Pada Express, kita mungkin mengenal middleware untuk membaca JSON.
Namun, karena kita menggunakan Node.js native, kita akan membaca body sendiri.
Objek req merupakan readable stream.
Artinya, data request dapat diterima dalam beberapa bagian atau chunk.
Kita perlu mengumpulkan bagian-bagian tersebut sebelum memprosesnya.
Buat function berikut:
async function bacaJSON(req) { const chunks = [];
for await (const chunk of req) { chunks.push(chunk); }
const body = Buffer.concat(chunks).toString("utf8");
return JSON.parse(body);}Mari kita pahami.
for await…of
Bagian:
for await (const chunk of req)Membaca data dari request stream secara asynchronous.
Setiap bagian data dimasukkan ke array chunks.
Buffer.concat()
Bagian:
Buffer.concat(chunks)Menggabungkan seluruh chunk menjadi satu Buffer.
toString(“utf8”)
Bagian:
.toString("utf8")Mengubah Buffer menjadi string.
JSON.parse()
Bagian:
JSON.parse(body)Mengubah teks JSON menjadi nilai JavaScript.
Jika body bukan JSON yang valid, JSON.parse() akan menghasilkan error.
Nanti kita akan menangani kondisi tersebut.
Catatan keamanan: function ini masih versi pembelajaran. Jika digunakan tanpa batas ukuran body, client dapat mengirim data sangat besar dan menghabiskan memori server. Pada implementasi lengkap, kita akan menambahkan batas ukuran request.
6. Mengenal Validasi Data
Sebelum membuat atau mengubah artikel, kita perlu memeriksa data yang diterima.
Untuk latihan ini, sebuah artikel harus memiliki:
judulberupa string yang tidak kosong.kategoriberupa string yang tidak kosong.
Contoh data valid:
{ "judul": "Belajar Backend", "kategori": "Programming"}Contoh data tidak valid:
{ "judul": "", "kategori": "Programming"}Kita buat function validasi:
function validasiArtikel(data) { if ( !data || typeof data !== "object" || Array.isArray(data) ) { return false; }
return ( typeof data.judul === "string" && data.judul.trim() !== "" && typeof data.kategori === "string" && data.kategori.trim() !== "" );}Function ini mengembalikan boolean.
true jika data memenuhi aturan.
false jika tidak.
Kita juga memastikan bahwa data merupakan object biasa untuk kebutuhan contoh, bukan null atau array.
Dalam aplikasi nyata, validasi dapat mencakup panjang string, format, karakter yang diperbolehkan, dan aturan bisnis lainnya.
7. Membuat Endpoint GET Semua Artikel
Sekarang kita mulai membuat routing.
Endpoint pertama:
GET /api/artikelTujuannya mengambil seluruh artikel.
Kode routing-nya:
if (req.method === "GET" && pathname === "/api/artikel") { kirimJSON(res, 200, artikel); return;}Ketika client mengirim request GET, server mengembalikan seluruh isi array.
Contoh response:
[ { "id": 1, "judul": "Belajar HTML", "kategori": "HTML" }, { "id": 2, "judul": "Belajar CSS", "kategori": "CSS" }, { "id": 3, "judul": "Belajar JavaScript", "kategori": "JavaScript" }]Status code yang digunakan adalah 200 OK.
8. Membuat Endpoint GET Detail Artikel
Sekarang kita ingin mengambil satu artikel berdasarkan ID.
Contohnya:
GET /api/artikel/2Kita perlu membaca angka 2 dari URL.
Salah satu cara sederhana adalah menggunakan regular expression.
const cocok = pathname.match(/^\/api\/artikel\/([1-9]\d*)$/);Jika URL sesuai pola, variabel cocok berisi hasil pencocokan.
Bagian:
cocok[1]Berisi nilai ID dalam bentuk string.
Kita ubah menjadi number:
const id = Number(cocok[1]);Kemudian cari artikel:
const data = artikel.find((item) => item.id === id);Jika ditemukan, kirim status 200.
Jika tidak ditemukan, kirim status 404.
Contoh routing:
if (req.method === "GET" && cocok) { const id = Number(cocok[1]);
const data = artikel.find((item) => item.id === id);
if (!data) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return; }
kirimJSON(res, 200, data); return;}Dengan demikian, endpoint dapat mengambil artikel tertentu berdasarkan ID.
9. Membuat Endpoint POST untuk Menambah Artikel
Sekarang kita masuk ke operasi Create.
Endpoint:
POST /api/artikelClient akan mengirim data JSON seperti:
{ "judul": "Belajar Backend", "kategori": "Programming"}Server perlu:
- Membaca request body.
- Memastikan JSON valid.
- Memvalidasi data.
- Membuat ID baru.
- Menambahkan artikel ke array.
- Mengirim response
201 Created.
Contoh logika utamanya:
const dataBaru = await bacaJSON(req);
if (!validasiArtikel(dataBaru)) { kirimJSON(res, 400, { pesan: "Judul dan kategori wajib diisi." }); return;}
const artikelBaru = { id: idBerikutnya++, judul: dataBaru.judul.trim(), kategori: dataBaru.kategori.trim()};
artikel.push(artikelBaru);
res.setHeader("Location", `/api/artikel/${artikelBaru.id}`);
kirimJSON(res, 201, artikelBaru);Mengapa menggunakan 201?
Karena request berhasil membuat resource baru.
Mengapa menggunakan Location?
Header Location menunjukkan URI resource yang baru dibuat.
Misalnya:
Location: /api/artikel/4Client dapat menggunakan URI tersebut untuk mengambil detail artikel.
10. Membuat Endpoint PUT untuk Mengubah Artikel
Sekarang kita ingin memperbarui artikel berdasarkan ID.
Endpoint:
PUT /api/artikel/2Client mengirim data pengganti:
{ "judul": "Belajar CSS Modern", "kategori": "CSS"}Untuk latihan ini, PUT akan mengganti field judul dan kategori pada artikel yang sudah ada.
Jika ID tidak ditemukan, API mengembalikan 404.
Contoh logikanya:
const id = Number(cocok[1]);
const index = artikel.findIndex((item) => item.id === id);
if (index === -1) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return;}
const dataBaru = await bacaJSON(req);
if (!validasiArtikel(dataBaru)) { kirimJSON(res, 400, { pesan: "Judul dan kategori wajib diisi." }); return;}
artikel[index] = { id, judul: dataBaru.judul.trim(), kategori: dataBaru.kategori.trim()};
kirimJSON(res, 200, artikel[index]);Perhatikan bahwa kita menggunakan findIndex() untuk mencari posisi artikel di dalam array.
Kemudian kita mengganti data pada posisi tersebut.
Pada implementasi ini, PUT hanya mendukung pembaruan artikel yang sudah ada.
Walaupun secara semantik HTTP PUT juga dapat digunakan untuk membuat resource pada URI tertentu, perilaku tersebut tidak kita terapkan pada latihan ini.
11. Membuat Endpoint DELETE untuk Menghapus Artikel
Sekarang kita masuk ke operasi Delete.
Endpoint:
DELETE /api/artikel/2Server perlu mencari artikel berdasarkan ID.
Jika ditemukan, hapus dari array.
Kita dapat menggunakan:
artikel = artikel.filter((item) => item.id !== id);Method filter() membuat array baru yang hanya berisi artikel dengan ID berbeda.
Contoh routing:
const id = Number(cocok[1]);
const data = artikel.find((item) => item.id === id);
if (!data) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return;}
artikel = artikel.filter((item) => item.id !== id);
res.writeHead(204);res.end();Status 204 No Content menunjukkan operasi berhasil tanpa body response.
Karena itu, kita tidak mengirim JSON setelah status 204.
12. Menangani JSON Tidak Valid
Bagaimana jika client mengirim body seperti ini?
{"judul": "Belajar Backend",}Format tersebut bukan JSON yang valid.
Jika langsung diproses menggunakan JSON.parse(), akan muncul SyntaxError.
Untuk menangani kondisi tersebut, kita bisa menggunakan try...catch.
Contohnya:
try { const data = await bacaJSON(req);
// Proses data} catch (error) { if (error instanceof SyntaxError) { kirimJSON(res, 400, { pesan: "Format JSON tidak valid." }); return; }
throw error;}Dengan demikian, request yang mengandung JSON tidak valid akan mendapatkan status 400 Bad Request.
Namun, kita juga perlu membedakan kesalahan format request dengan error internal server.
Pada versi lengkap nanti, kita akan membuat penanganan error yang lebih terstruktur.
13. Membatasi Ukuran Request Body
Membaca seluruh request body tanpa batas ukuran dapat menimbulkan masalah.
Misalnya, client mengirim body berukuran sangat besar.
Jika server terus mengumpulkan data ke memori, penggunaan memori dapat meningkat.
Untuk latihan ini, kita akan membatasi body JSON maksimal 10 KB.
Buat function:
async function bacaJSON(req) { const batasUkuran = 10 * 1024; const chunks = []; let totalUkuran = 0;
for await (const chunk of req) { totalUkuran += chunk.length;
if (totalUkuran > batasUkuran) { const error = new Error("Ukuran request terlalu besar."); error.statusCode = 413; throw error; }
chunks.push(chunk); }
const body = Buffer.concat(chunks).toString("utf8");
try { return JSON.parse(body); } catch { const error = new Error("Format JSON tidak valid."); error.statusCode = 400; throw error; }}Di sini kita memperkenalkan dua status code:
400 Bad Request: body bukan JSON yang valid.413 Content Too Large: body melebihi batas yang diizinkan.
Batas 10 KB hanya untuk latihan.
Pada aplikasi nyata, batas ukuran perlu disesuaikan dengan jenis data dan kebutuhan aplikasi.
Penting juga untuk memahami bahwa ketika stream request dihentikan lebih awal akibat batas ukuran, koneksi dan respons harus ditangani dengan benar.
Karena itu, implementasi produksi membutuhkan pengelolaan stream, timeout, dan koneksi yang lebih matang.
14. Membuat REST API Lengkap Tanpa Express
Sekarang kita gabungkan seluruh bagian menjadi satu file server.js.
Kode berikut menggunakan:
- Modul
node:http. - Array sebagai penyimpanan sementara.
- Routing berdasarkan method dan pathname.
- Pembacaan body JSON.
- Validasi data.
- Penanganan error.
- Batas ukuran body.
Ganti seluruh isi server.js dengan kode berikut:
const http = require("node:http");
const HOST = "127.0.0.1";const PORT = 3000;const BATAS_BODY = 10 * 1024;
let artikel = [ { id: 1, judul: "Belajar HTML", kategori: "HTML" }, { id: 2, judul: "Belajar CSS", kategori: "CSS" }, { id: 3, judul: "Belajar JavaScript", kategori: "JavaScript" }];
let idBerikutnya = 4;
function kirimJSON(res, statusCode, data) { res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
res.end(JSON.stringify(data));}
function buatError(statusCode, pesan) { const error = new Error(pesan); error.statusCode = statusCode; return error;}
async function bacaJSON(req) { const tipeKonten = req.headers["content-type"] || "";
if (!/^application\/json(?:\s*;|$)/i.test(tipeKonten)) { throw buatError( 415, "Gunakan Content-Type: application/json." ); }
const chunks = []; let totalUkuran = 0; let terlaluBesar = false;
for await (const chunk of req) { totalUkuran += chunk.length;
if (totalUkuran > BATAS_BODY) { terlaluBesar = true; }
if (!terlaluBesar) { chunks.push(chunk); } }
if (terlaluBesar) { throw buatError(413, "Ukuran request terlalu besar."); }
const body = Buffer.concat(chunks).toString("utf8");
try { return JSON.parse(body); } catch { throw buatError(400, "Format JSON tidak valid."); }}
function validasiArtikel(data) { if ( !data || typeof data !== "object" || Array.isArray(data) ) { return false; }
return ( typeof data.judul === "string" && data.judul.trim() !== "" && typeof data.kategori === "string" && data.kategori.trim() !== "" );}
async function tanganiRequest(req, res) { const url = new URL(req.url, "http://localhost"); const pathname = url.pathname; const method = req.method;
const cocok = pathname.match(/^\/api\/artikel\/([1-9]\d*)$/);
if (method === "GET" && pathname === "/api/artikel") { kirimJSON(res, 200, artikel); return; }
if (method === "GET" && cocok) { const id = Number(cocok[1]); const data = artikel.find((item) => item.id === id);
if (!data) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return; }
kirimJSON(res, 200, data); return; }
if (method === "POST" && pathname === "/api/artikel") { const dataBaru = await bacaJSON(req);
if (!validasiArtikel(dataBaru)) { kirimJSON(res, 400, { pesan: "Judul dan kategori wajib diisi." }); return; }
const artikelBaru = { id: idBerikutnya++, judul: dataBaru.judul.trim(), kategori: dataBaru.kategori.trim() };
artikel.push(artikelBaru);
res.setHeader( "Location", `/api/artikel/${artikelBaru.id}` );
kirimJSON(res, 201, artikelBaru); return; }
if (method === "PUT" && cocok) { const id = Number(cocok[1]);
const index = artikel.findIndex((item) => item.id === id);
if (index === -1) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return; }
const dataBaru = await bacaJSON(req);
if (!validasiArtikel(dataBaru)) { kirimJSON(res, 400, { pesan: "Judul dan kategori wajib diisi." }); return; }
artikel[index] = { id, judul: dataBaru.judul.trim(), kategori: dataBaru.kategori.trim() };
kirimJSON(res, 200, artikel[index]); return; }
if (method === "DELETE" && cocok) { const id = Number(cocok[1]);
const data = artikel.find((item) => item.id === id);
if (!data) { kirimJSON(res, 404, { pesan: "Artikel tidak ditemukan." }); return; }
artikel = artikel.filter((item) => item.id !== id);
res.writeHead(204); res.end(); return; }
const pathArtikel = pathname === "/api/artikel" || /^\/api\/artikel\/[^/]+$/.test(pathname);
if (pathArtikel) { res.setHeader("Allow", "GET, POST, PUT, DELETE");
kirimJSON(res, 405, { pesan: "Method tidak diizinkan untuk endpoint ini." }); return; }
kirimJSON(res, 404, { pesan: "Endpoint tidak ditemukan." });}
const server = http.createServer((req, res) => { tanganiRequest(req, res).catch((error) => { console.error("Error:", error.message);
if (res.headersSent) { res.destroy(); return; }
const statusCode = Number.isInteger(error.statusCode) && error.statusCode >= 400 && error.statusCode < 500 ? error.statusCode : 500;
const pesan = statusCode === 500 ? "Terjadi kesalahan pada server." : error.message;
kirimJSON(res, statusCode, { pesan }); });});
server.on("error", (error) => { console.error("Server error:", error.message);});
server.listen(PORT, HOST, () => { console.log(`REST API berjalan di http://localhost:${PORT}`);});Sekarang kita mempunyai API CRUD sederhana tanpa framework.
Mengapa menggunakan async function?
Karena pembacaan request body merupakan proses asynchronous.
Dengan async dan await, kita bisa menulis alur pembacaan data secara lebih mudah dipahami.
Kita akan membahas Promise dan async/await lebih mendalam pada artikel berikutnya.
Mengapa ada .catch() pada tanganiRequest()?
Function tanganiRequest() merupakan async function.
Ketika terjadi error yang tidak ditangani di dalamnya, function tersebut menghasilkan Promise yang ditolak.
Dengan .catch(), kita dapat menangani error tersebut dan mengirim respons yang sesuai.
Catatan mengenai batas ukuran body
Pada implementasi lengkap ini, ketika ukuran body melebihi batas, server berhenti menyimpan chunk tambahan tetapi tetap membaca sisa stream sampai selesai sebelum mengirim status 413.
Pendekatan tersebut membantu menjaga penggunaan memori body tetap terbatas untuk contoh pembelajaran.
Namun, client yang mengirim data sangat lambat tetap dapat menahan koneksi.
Untuk aplikasi produksi, kita perlu menambahkan pengaturan timeout, batas koneksi, dan strategi penanganan request yang lebih matang.
Catatan mengenai status 405
Contoh kode menggunakan penanganan 405 yang disederhanakan.
Pada API produksi, header Allow sebaiknya mencerminkan method yang benar-benar didukung oleh resource target.
Misalnya, /api/artikel mendukung GET dan POST, sedangkan /api/artikel/1 mendukung GET, PUT, dan DELETE.
Kita sengaja menjaga routing tetap sederhana agar fokus pada CRUD.
15. Menjalankan REST API
Simpan file server.js.
Kemudian buka terminal pada folder proyek.
Jalankan:
node server.jsJika berhasil, akan muncul:
REST API berjalan di http://localhost:3000Sekarang server siap menerima request.
Karena kita menggunakan beberapa HTTP method, pengujian tidak cukup hanya melalui address bar browser.
Kita akan menggunakan curl.
16. Menguji GET Semua Artikel
Jalankan:
curl -i http://localhost:3000/api/artikelJika berhasil, status response adalah:
200 OKBody berisi daftar artikel.
Kita juga bisa membuka endpoint tersebut langsung melalui browser karena menggunakan method GET.
17. Menguji GET Detail Artikel
Jalankan:
curl -i http://localhost:3000/api/artikel/2Jika artikel tersedia, server mengembalikan data artikel dengan ID 2.
Coba juga:
curl -i http://localhost:3000/api/artikel/999Karena artikel dengan ID tersebut tidak tersedia, server mengembalikan:
404 Not FoundDengan body:
{ "pesan": "Artikel tidak ditemukan."}18. Menguji POST untuk Menambah Artikel
Sekarang kita coba menambahkan artikel baru.
Pada terminal yang mendukung sintaks curl berikut, jalankan:
curl -i -X POST http://localhost:3000/api/artikel \ -H "Content-Type: application/json" \ -d '{"judul":"Belajar Backend","kategori":"Programming"}'Pada Windows PowerShell, contoh yang lebih mudah digunakan adalah:
curl.exe -i -X POST http://localhost:3000/api/artikel -H "Content-Type: application/json" -d '{\"judul\":\"Belajar Backend\",\"kategori\":\"Programming\"}'Catatan: perilaku escaping tanda kutip dapat berbeda antara versi PowerShell dan cara curl menerima argumen. Jika contoh PowerShell mengalami masalah quoting, gunakan file JSON sebagai alternatif yang lebih konsisten.
Buat file artikel-baru.json berisi:
{ "judul": "Belajar Backend", "kategori": "Programming"}Kemudian jalankan:
curl.exe -i -X POST http://localhost:3000/api/artikel -H "Content-Type: application/json" --data-binary "@artikel-baru.json"Jika berhasil, server mengembalikan status:
201 CreatedDengan data artikel baru.
Misalnya:
{ "id": 4, "judul": "Belajar Backend", "kategori": "Programming"}Nilai ID dapat berbeda jika kita sudah menambahkan artikel sebelumnya.
19. Menguji PUT untuk Mengubah Artikel
Sekarang kita ubah artikel dengan ID 2.
Contoh request:
curl -i -X PUT http://localhost:3000/api/artikel/2 \ -H "Content-Type: application/json" \ -d '{"judul":"Belajar CSS Modern","kategori":"CSS"}'Untuk PowerShell, kita juga bisa menggunakan file JSON.
Buat file artikel-update.json:
{ "judul": "Belajar CSS Modern", "kategori": "CSS"}Kemudian jalankan:
curl.exe -i -X PUT http://localhost:3000/api/artikel/2 -H "Content-Type: application/json" --data-binary "@artikel-update.json"Jika berhasil, server mengembalikan data artikel yang sudah diperbarui.
Coba ambil kembali:
curl http://localhost:3000/api/artikel/2Judul artikel seharusnya sudah berubah.
20. Menguji DELETE untuk Menghapus Artikel
Sekarang kita hapus artikel dengan ID 2.
Jalankan:
curl -i -X DELETE http://localhost:3000/api/artikel/2Jika berhasil, server mengembalikan:
204 No ContentKemudian coba:
curl -i http://localhost:3000/api/artikel/2Server akan mengembalikan:
404 Not FoundKarena artikel tersebut sudah dihapus dari array.
21. Menguji Validasi dan Error
API yang baik tidak hanya menangani request yang benar.
Kita juga perlu memeriksa perilakunya ketika menerima request yang tidak sesuai.
Judul kosong
Kirim:
{ "judul": "", "kategori": "Programming"}Server seharusnya mengembalikan:
400 Bad RequestJSON tidak valid
Kirim body:
{"judul":"Belajar Backend",}Server seharusnya mengembalikan:
400 Bad RequestContent-Type salah
Jika mengirim body menggunakan:
Content-Type: text/plainKe endpoint yang mengharapkan JSON, server akan mengembalikan:
415 Unsupported Media TypeArtikel tidak ditemukan
Jika mengakses:
GET /api/artikel/999Server mengembalikan:
404 Not FoundRequest body terlalu besar
Jika body melebihi batas 10 KB, server akan mengembalikan:
413 Content Too LargeSetelah seluruh pengujian, coba jalankan ulang server.
Data akan kembali seperti semula karena masih disimpan di memori.
22. Mengapa Data Hilang Ketika Server Restart?
Karena kita menyimpan data menggunakan array:
let artikel = [];Array tersebut berada di memori proses Node.js.
Ketika proses dihentikan, data yang hanya berada di memori tidak dipertahankan secara otomatis.
Ketika server dijalankan kembali, kode inisialisasi akan membuat array dari data awal.
Untuk aplikasi sungguhan, kita biasanya membutuhkan penyimpanan yang lebih permanen.
Contohnya database.
Namun, sebelum masuk ke database, kita perlu memahami bagaimana frontend berkomunikasi dengan API yang sudah dibuat.
Itulah materi pada artikel berikutnya.
23. Kesalahan yang Sering Terjadi
Lupa menggunakan await ketika membaca body
Perhatikan:
const data = bacaJSON(req);Karena bacaJSON() merupakan async function, hasilnya adalah Promise.
Jika ingin mendapatkan hasil akhirnya, gunakan:
const data = await bacaJSON(req);Di dalam async function.
Menggunakan JSON.parse() tanpa penanganan error
Request body berasal dari client.
Jangan menganggap seluruh body pasti berisi JSON valid.
Tidak memvalidasi data
Walaupun frontend sudah melakukan validasi, backend tetap perlu memeriksa data.
Mengirim response dua kali
Setelah mengirim response, pastikan alur callback berhenti atau berpindah ke bagian yang tepat.
Menggunakan status 200 untuk semua hasil
Gunakan status code sesuai kondisi.
Misalnya:
201untuk resource baru.400untuk request tidak valid.404untuk resource tidak ditemukan.500untuk error internal.
Menganggap API ini siap digunakan di internet
Belum.
API ini dirancang untuk pembelajaran lokal.
Belum ada autentikasi, otorisasi, pembatasan request, konfigurasi deployment, maupun penyimpanan permanen.
Jangan membuka API CRUD ini ke internet tanpa menambahkan perlindungan yang sesuai.
24. Latihan Mandiri
Sekarang coba kembangkan API yang sudah dibuat.
Latihan 1: Menambahkan field penulis
Ubah struktur artikel menjadi:
{ "id": 1, "judul": "Belajar HTML", "kategori": "HTML", "penulis": "BapakGabut"}Pastikan validasi POST dan PUT ikut diperbarui.
Latihan 2: Menambahkan filter kategori
Buat endpoint:
GET /api/artikel?kategori=HTMLYang hanya mengembalikan artikel dengan kategori sesuai.
Gunakan:
url.searchParams.get("kategori")Untuk membaca query parameter.
Latihan 3: Membuat API game
Buat resource baru:
/api/gameDengan field:
{ "id": 1, "judul": "Game Favorit", "platform": "Nintendo Switch"}Kemudian buat operasi CRUD yang serupa.
Latihan 4: Menangani ID tidak valid
Coba akses:
GET /api/artikel/abcSaat ini, routing contoh tidak mengenali path tersebut sebagai detail artikel valid.
Coba kembangkan penanganannya agar client mendapatkan respons yang jelas mengenai format ID yang tidak sesuai.
Latihan 5: Menambahkan batas panjang judul
Tambahkan aturan:
- Judul maksimal 150 karakter.
- Kategori maksimal 50 karakter.
Pastikan request yang melanggar aturan mendapatkan status 400.
25. Penutup
Selamat, Gabuters!
Kita berhasil membuat REST API sederhana menggunakan JavaScript dan Node.js native tanpa framework.
API kita sudah mendukung operasi CRUD:
GETuntuk membaca data.POSTuntuk menambahkan data.PUTuntuk memperbarui data.DELETEuntuk menghapus data.
Kita juga mempelajari cara membaca request body, menggunakan async/await, memvalidasi data, membatasi ukuran request, dan mengirim status code yang sesuai.
Walaupun data masih disimpan di memori, kita sudah mempunyai fondasi backend yang dapat mengelola resource.
Sekarang muncul pertanyaan berikutnya.
Bagaimana cara menghubungkan halaman HTML dan JavaScript di browser dengan API yang baru kita buat?
Bagaimana cara mengirim request tanpa menggunakan terminal?
Bagaimana cara menampilkan hasil response langsung di halaman?
Pada artikel berikutnya, kita akan belajar Menghubungkan Frontend ke Backend dengan Fetch API.
Kita juga akan membahas Promise, async/await, loading state, dan penanganan error ketika frontend berkomunikasi dengan backend.
Sampai jumpa di artikel berikutnya, Gabuters!
Bagikan Artikel
Buat poster berbagi atau salin tautan artikel ini.
Lanjut membaca
Coba jalur lain
Pilihan konsisten dari artikel lain
Terakhir diperbarui pada , 21 hari yang lalu
Sebagian konten mungkin sudah tidak berlaku