"Sebelum kamu bisa membangun rumah, kamu harus tahu dulu bagaimana bata, semen, dan pondasi bekerja."
Tentang Artikel Ini
Hampir semua tutorial backend dimulai langsung dari kode — "install Express, buat endpoint, return JSON." Tapi ada satu langkah yang sering dilewati: memahami mengapa semua itu bekerja.
Apa sebenarnya yang terjadi ketika browser mengirim request? Apa bedanya GET dengan POST? Kenapa ada status code 404 dan 500? Apa itu header? Kenapa response harus JSON? Apa itu REST?
Saya pribadi baru benar-benar paham jadi backend developer setelah bisa jawab semua pertanyaan di atas — bukan cuma bisa pakai Express atau Fastify, tapi ngerti kenapa request bisa gagal, kenapa CORS tiba-tiba marah, kenapa API kadang lemot padahal kodenya sudah benar. Di artikel ini saya coba jelaskan itu semua, bukan dengan teori yang berat, tapi dengan analogi yang saya pakai sendiri waktu belajar dulu, dan contoh yang bisa langsung kamu coba sendiri. Setelah selesai, kamu akan punya mental model yang sama yang saya pakai tiap kali debug masalah backend — fondasi yang membuat semua artikel berikutnya terasa jauh lebih masuk akal.
Prasyarat: Tidak ada. Artikel ini dirancang untuk siapa saja yang ingin belajar backend dari nol.
Daftar Isi
- Bagaimana Internet Bekerja
- HTTP: Bahasa Komputer di Internet
- HTTP Methods: Kata Kerja di API
- Status Codes yang Harus Kamu Hafal
- Headers: Metadata di Setiap Request
- JSON: Format Data di Web Modern
- REST: Konvensi Membangun API
- Coba Sendiri: curl & Hoppscotch
Bab 1: Bagaimana Internet Bekerja
Analogi Warung Makan
Bayangkan kamu pergi ke warung makan. Kamu datang, duduk, lalu pesan ke pelayan: "Satu nasi goreng." Pelayan mencatat, menyampaikan ke dapur, dapur masak, lalu makanan diantarkan ke mejamu.
Inilah persis yang terjadi ketika browser kamu membuka sebuah website:
- Kamu = browser (Chrome, Firefox, Safari)
- Pelayan = HTTP protocol
- Dapur = server (komputer yang menyimpan website)
- Pesanan = HTTP request
- Makanan yang diantar = HTTP response
Dari Ketik URL sampai Halaman Muncul
Ketika kamu mengetik https://backendlabs.id dan menekan Enter, ada rangkaian kejadian di baliknya yang berlangsung dalam hitungan milidetik — dan begitu kamu tahu urutannya, kamu jadi tahu persis di titik mana harus curiga kalau nanti ada yang terasa lambat atau error. Urutannya kira-kira begini:
Langkah 1 — DNS Lookup
Browser tidak tahu di mana server backendlabs.id berada. Ia bertanya ke DNS (Domain Name System) — semacam buku telepon raksasa di internet.
Browser → DNS Resolver (biasanya dari ISP atau 1.1.1.1)
→ Root Nameserver → .id Nameserver → backendlabs.id Nameserver
← IP Address: 103.28.12.45
DNS mengubah nama domain yang mudah diingat manusia menjadi IP address — alamat numerik yang dipakai komputer untuk saling menemukan. Hasil lookup di-cache di browser, OS, dan DNS resolver sehingga lookup berikutnya lebih cepat.
Langkah 2 — TCP Connection (3-Way Handshake)
Setelah tahu IP address-nya, langkah berikutnya adalah membangun koneksi memakai TCP (Transmission Control Protocol) — protokol yang memastikan data terkirim dengan benar dan berurutan, lewat proses yang biasa disebut jabat tangan tiga langkah:
Browser → [SYN] → Server "Hei, saya mau konek"
Browser ← [SYN-ACK] ← Server "Oke, saya siap"
Browser → [ACK] → Server "Siap, kita mulai"
Seluruh handshake ini selesai dalam hitungan milidetik, tapi latency jaringan tetap berpengaruh — ini kenapa memilih server yang dekat secara geografis penting untuk performa.
Langkah 3 — TLS Handshake (khusus HTTPS)
Kalau situsnya pakai HTTPS, ada satu lapisan tambahan sebelum data mulai mengalir: TLS (Transport Layer Security). Setelah koneksi TCP terbentuk, browser dan server saling bernegosiasi soal enkripsi yang akan dipakai:
Browser → "Saya support TLS 1.3, ini cipher yang bisa saya pakai"
Server ← "Oke pakai TLS 1.3 + AES-256. Ini sertifikat SSL saya."
Browser → [verifikasi sertifikat ke Certificate Authority]
Browser → "Sertifikat valid. Mari enkripsi komunikasi kita."
Setelah handshake selesai, semua data yang dikirim terenkripsi. Bahkan ISP atau siapapun yang "menyadap" jaringan hanya melihat data yang tidak bisa dibaca.
Langkah 4 — HTTP Request
Browser mengirimkan request:
GET / HTTP/2
Host: backendlabs.id
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: text/html,application/xhtml+xml
Accept-Language: id-ID,id;q=0.9,en;q=0.8Langkah 5 — HTTP Response
Server membalas dengan mengirimkan halaman web:
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 12453
Cache-Control: public, max-age=3600
<!DOCTYPE html>
<html lang="id">...</html>Langkah 6 — Render
Browser menerima HTML itu, lalu jalan lagi: meminta CSS, JavaScript, gambar, dan aset lain satu per satu (masing-masing request HTTP terpisah), sebelum akhirnya merender semuanya jadi halaman yang kamu lihat di layar.
HTTP vs HTTPS
Satu huruf S itu kelihatannya sepele, tapi bedanya besar sekali — dan ini salah satu hal yang menurut saya wajib dipahami sebelum menyentuh baris kode apapun.
HTTP mengirim semuanya dalam bentuk plain text. Siapapun yang berada di jaringan yang sama denganmu — WiFi kafe adalah contoh klasik — bisa membaca isi data yang lewat, termasuk password dan token yang seharusnya rahasia.
HTTPS membungkus komunikasi itu dengan enkripsi TLS, jadi meskipun ada yang menyadap, yang terlihat cuma data acak yang tidak bisa dibaca.
Browser modern sekarang cukup galak soal ini: ikon gembok muncul untuk HTTPS, sementara HTTP dapat peringatan "Not Secure" yang cukup mencolok. Aturan yang saya pegang sederhana: pakai HTTPS untuk semua aplikasi, tanpa kecuali — apalagi yang menangani data pengguna.
HTTP/1.1, HTTP/2, HTTP/3
Protokol HTTP juga terus berevolusi, dan tiap versi baru lahir untuk menambal keterbatasan versi sebelumnya.
HTTP/1.1 cuma bisa mengirim satu request per koneksi TCP dalam satu waktu (pipelining sebenarnya ada, tapi jarang benar-benar dipakai karena implementasinya rewel). Browser menyiasatinya dengan membuka sampai 6 koneksi paralel per domain — cara yang bekerja, tapi jelas bukan yang paling efisien.
HTTP/2 memperbaiki itu dengan multiplexing: banyak request bisa berjalan bersamaan dalam satu koneksi TCP, dan header-nya dikompresi pakai HPACK supaya lebih ringkas. Ini yang dipakai kebanyakan website modern sekarang.
HTTP/3 melangkah lebih jauh lagi — TCP diganti QUIC yang berbasis UDP. Hasilnya, koneksi jadi lebih tahan banting di jaringan yang tidak stabil seperti mobile, dan lebih toleran terhadap packet loss. Adopsinya masih berjalan, belum semerata HTTP/2.
Kabar baiknya buat kamu sebagai backend developer: kamu hampir tidak pernah perlu mengimplementasikan HTTP/2 atau HTTP/3 secara manual. Nginx atau reverse proxy lain yang biasanya menanganinya di depan aplikasimu.
Bab 2: HTTP: Bahasa Komputer di Internet
Anatomi HTTP Request
Begitu kamu bongkar, setiap HTTP request itu sebenarnya cuma terdiri dari tiga bagian utama:
[Method] [Path] HTTP/[version] ← Request Line
[Header-Name]: [Header-Value] ← Headers (satu per baris)
← Baris kosong (memisahkan header dan body)
[Body] ← Body (opsional)
Contoh request login:
POST /api/v1/auth/login HTTP/1.1
Host: api.backendlabs.id
Content-Type: application/json
Content-Length: 56
Authorization: Bearer eyJhbGci...
Accept: application/json
{"email":"rinaldy@email.com","password":"secret123"}Komponen-komponennya:
POST— method/api/v1/auth/login— path (endpoint)HTTP/1.1— versi protokolHostsampaiAccept— headers (metadata){"email":...}— body (data yang dikirim)
Anatomi HTTP Response
HTTP/[version] [Status Code] [Reason Phrase] ← Status Line
[Header-Name]: [Header-Value] ← Headers
← Baris kosong
[Body] ← Body (opsional)
Contoh response sukses:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 287
X-Request-Id: req_abc123
{
"success": true,
"data": {
"user": { "id": 1, "name": "Rinaldy", "email": "rinaldy@email.com" },
"accessToken": "eyJhbGci...",
"refreshToken": "dGhpcyBpcy..."
}
}Anatomy URL Lengkap
https://api.backendlabs.id:443/v1/products?category=backend&sort=price&page=2#results
│ │ │ │ │ │
│ │ │ │ └── Query String │
│ │ │ └── Path │
│ │ └── Port (443 = HTTPS default, bisa dihilangkan) │
│ └── Host (domain) │
└── Scheme └── Fragment (tidak dikirim ke server)
Query string dimulai dengan ? dan berisi pasangan key=value yang dipisahkan &. Ini tempatnya untuk filter, sorting, pagination — pokoknya data yang tidak mengubah state server, makanya cuma relevan untuk GET.
Bab 3: HTTP Methods: Kata Kerja di API
Mengapa Ada Banyak Method?
Ini salah satu bagian yang, begitu klik di kepala, membuat API terasa jauh lebih masuk akal buat saya — dan sering jadi pembeda antara API yang enak dipakai dengan yang bikin bingung. HTTP methods menjelaskan apa yang ingin kamu lakukan terhadap sebuah resource. URL adalah benda (apa yang diakses), method adalah kata kerja (apa yang dilakukan).
Tanpa method yang berbeda, server tidak bisa membedakan apakah kamu ingin membaca produk atau menghapus produk — keduanya bisa dikirim ke URL yang sama (/api/products/5).
GET — Ambil Data
GET adalah method yang paling sering kamu pakai tanpa sadar — setiap kali membuka halaman web, browser mengirim GET di belakang layar. Fungsinya membaca data, dan aturannya tegas: request GET tidak boleh mengubah apapun di server.
GET /api/v1/products HTTP/1.1 ← semua produk
GET /api/v1/products/42 HTTP/1.1 ← produk dengan ID 42
GET /api/v1/products?category=backend ← filter by kategoriKarakteristik GET:
- Tidak punya body
- Parameter dikirim via query string
- Bisa di-cache oleh browser dan CDN
- Safe — tidak mengubah state server
- Idempotent — memanggil berkali-kali menghasilkan respons yang sama
POST — Buat Data Baru
Begitu kamu butuh membuat sesuatu yang baru — user baru, produk baru, order baru — POST adalah method yang dipakai. Ia juga sering dipakai sekadar untuk mengirim data yang perlu diproses server, meski tidak selalu berujung pada resource baru.
POST /api/v1/products HTTP/1.1
Content-Type: application/json
{
"name": "Ebook Docker Backend",
"price": 99000,
"category": "backend"
}Karakteristik POST:
- Punya body berisi data
- Tidak idempotent — mengirim dua kali → buat dua resource berbeda
- Tidak bisa di-cache
- Response biasanya:
201 Created+ headerLocationyang menunjuk ke resource baru
PUT — Ganti Keseluruhan
PUT ini yang menurut saya paling sering disalahpahami pemula. Fungsinya mengganti seluruh representasi resource — bukan sekadar update sebagian. Konsekuensinya: kamu harus kirim semua field, bukan cuma yang berubah.
PUT /api/v1/products/42 HTTP/1.1
Content-Type: application/json
{
"name": "Ebook Docker Backend — Edisi Revisi",
"price": 119000,
"category": "backend",
"description": "Deskripsi lengkap baru..."
}Ini bukan cuma teori di atas kertas — saya pernah menemukan kasus di mana tim frontend mengira PUT bisa dipakai seperti PATCH, kirim cuma { "price": 119000 }, dan field lain di resource itu ikut terhapus karena implementasi server memang menganggap field yang tidak dikirim berarti dihapus.
PATCH — Update Sebagian
PATCH adalah versi yang lebih "sopan" dibanding PUT — kamu cuma kirim field yang berubah, sisanya dibiarkan apa adanya.
PATCH /api/v1/products/42 HTTP/1.1
Content-Type: application/json
{ "price": 119000 }Praktiknya, PATCH inilah yang paling sering saya pakai untuk update sehari-hari. PUT baru relevan kalau kamu memang berniat mengganti seluruh resource sekaligus.
DELETE — Hapus
Sesuai namanya — dipakai untuk menghapus resource. Tidak ada yang rumit di sini.
DELETE /api/v1/products/42 HTTP/1.1Setelah berhasil, server biasanya membalas dengan 204 No Content — sukses, tapi tidak ada body untuk dikirim balik — atau 200 OK disertai pesan konfirmasi kalau kamu ingin lebih eksplisit.
Ringkasan: Safe & Idempotent
Dua istilah ini sering nongol di dokumentasi API dan awalnya kedengaran abstrak, padahal maksudnya sederhana. Safe berarti method itu tidak mengubah apapun di server — kamu bisa panggil sesukanya tanpa efek samping. Idempotent berarti memanggilnya berkali-kali menghasilkan efek yang sama seperti memanggil sekali saja — berguna sekali waktu retry request yang gagal karena network timeout, karena kamu tidak perlu khawatir memicu efek ganda.
| Method | Body | Safe | Idempotent | Response sukses |
|---|---|---|---|---|
| GET | ✗ | ✓ | ✓ | 200 OK |
| POST | ✓ | ✗ | ✗ | 201 Created |
| PUT | ✓ | ✗ | ✓ | 200 OK |
| PATCH | ✓ | ✗ | ✗ (umumnya) | 200 OK |
| DELETE | ✗ | ✗ | ✓ | 204 No Content |
Bab 4: Status Codes yang Harus Kamu Hafal
Status code itu angka tiga digit, tapi jangan anggap remeh — dari situ saja kamu bisa langsung tahu apakah request berhasil dan apa yang kira-kira terjadi di server, tanpa perlu baca body response dulu. Digit pertama adalah kuncinya: itu yang menentukan kategori.
2xx — Sukses
Ini kategori yang paling enak dilihat di log — artinya request berhasil diproses.
| Code | Text | Kapan dipakai |
|---|---|---|
| 200 | OK | Request berhasil — respons standar untuk GET, PUT, PATCH |
| 201 | Created | Resource baru berhasil dibuat (POST) |
| 202 | Accepted | Request diterima tapi belum selesai diproses (async job) |
| 204 | No Content | Berhasil, tidak ada data yang dikembalikan (DELETE, PATCH tanpa body) |
3xx — Redirect
Kategori ini bukan berarti gagal, cuma perlu mampir dulu ke URL lain.
| Code | Text | Kapan dipakai |
|---|---|---|
| 301 | Moved Permanently | URL sudah pindah permanen — browser akan update bookmark |
| 302 | Found | Redirect sementara — browser tidak update bookmark |
| 304 | Not Modified | Resource tidak berubah, gunakan cache yang ada |
4xx — Error dari Client
Di sinilah client yang jadi biang keladinya — request yang dikirim ada yang salah, dan ini penting dibedakan: bukan kesalahan server.
| Code | Text | Kapan dipakai |
|---|---|---|
| 400 | Bad Request | Request tidak valid: format JSON salah, field kurang/salah tipe |
| 401 | Unauthorized | Belum autentikasi — token tidak ada, expired, atau invalid |
| 403 | Forbidden | Sudah autentikasi tapi tidak punya izin untuk resource ini |
| 404 | Not Found | Resource tidak ditemukan di server |
| 405 | Method Not Allowed | Method tidak didukung untuk endpoint ini |
| 409 | Conflict | Konflik data: email sudah terdaftar, duplicate entry |
| 410 | Gone | Resource pernah ada tapi sudah dihapus permanen |
| 422 | Unprocessable Entity | Data bisa di-parse tapi gagal validasi bisnis |
| 429 | Too Many Requests | Rate limit terlampaui — coba lagi nanti |
5xx — Error dari Server
Kebalikan dari 4xx — di sini justru server yang gagal memproses request yang sebenarnya sudah benar. Ini kesalahan server, bukan client, dan biasanya yang bikin was-was kalau muncul di production.
| Code | Text | Kapan dipakai |
|---|---|---|
| 500 | Internal Server Error | Error tak terduga — bug di kode, exception tidak tertangani |
| 502 | Bad Gateway | Server upstream (database, microservice) tidak merespons |
| 503 | Service Unavailable | Server kelebihan beban atau sedang maintenance |
| 504 | Gateway Timeout | Upstream tidak merespons dalam waktu yang ditentukan |
Perbedaan yang Sering Membingungkan
400 vs 422: Saya pakai aturan sederhana untuk membedakan keduanya — 400 untuk request yang bahkan tidak bisa diparsing sama sekali (JSON-nya malformed), sementara 422 untuk data yang formatnya sudah benar tapi gagal validasi bisnis (email formatnya valid tapi domainnya tidak terdaftar, harga negatif, dan semacamnya).
401 vs 403:
401 Unauthorized → "Siapa kamu? Tolong buktikan identitasmu dulu."
403 Forbidden → "Saya tahu kamu siapa, tapi kamu tidak boleh masuk sini."
Login user biasa lalu akses /api/admin/users → 403, bukan 401.
404 vs 410: 410 Gone dipakai kalau resource itu pernah ada dan sengaja dihapus permanen — semacam pemberitahuan "sudah tidak ada lagi, dan memang tidak akan kembali". 404 lebih umum: dipakai untuk resource yang memang tidak pernah ada, atau yang sekadar tidak ditemukan.
Error Response yang Baik
Status code saja sering tidak cukup. Kalau saya jadi developer yang mengonsumsi API-mu, saya juga butuh detail yang membantu saya debug tanpa harus menebak-nebak:
// ✗ Kurang informatif
HTTP 400 Bad Request
{}
// ✓ Informatif
HTTP 422 Unprocessable Entity
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Beberapa field tidak valid",
"details": [
{ "field": "email", "message": "Format email tidak valid" },
{ "field": "price", "message": "Harga harus angka positif" }
]
}
}Bab 5: Headers: Metadata di Setiap Request
Apa Itu Header?
Header itu semacam catatan tempel yang menyertai setiap HTTP request dan response — informasi tambahan yang berjalan bersama data utama di body, tapi terpisah darinya. Formatnya konsisten: Nama: Nilai, dan nama header tidak case-sensitive, jadi Content-Type dan content-type dianggap sama saja.
Headers Request Penting
Content-Type — format data di body:
Content-Type: application/json
Content-Type: multipart/form-data; boundary=----FormBoundary7MA4YWxkTrZu0gW
Content-Type: application/x-www-form-urlencodedAuthorization — token autentikasi:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Authorization: Basic dXNlcjpwYXNzd29yZA== ← Base64(user:password)
Authorization: ApiKey sk-abc123xyzAccept — format response yang diinginkan:
Accept: application/json
Accept: text/html, application/xhtml+xml, */*Content-Length — ukuran body dalam bytes:
Content-Length: 156Accept-Language — preferensi bahasa:
Accept-Language: id-ID, id;q=0.9, en;q=0.8Headers Response Penting
Content-Type — format data yang dikembalikan:
Content-Type: application/json; charset=utf-8
Content-Type: text/html; charset=utf-8
Content-Type: image/pngLocation — URL resource yang baru dibuat (bersama 201 Created):
HTTP/1.1 201 Created
Location: /api/v1/products/43Cache-Control — instruksi caching:
Cache-Control: no-cache ← selalu validasi ke server sebelum gunakan cache
Cache-Control: no-store ← jangan cache sama sekali (data sensitif)
Cache-Control: max-age=3600 ← cache boleh dipakai sampai 1 jam
Cache-Control: public, max-age=86400 ← bisa di-cache CDN selama 24 jamETag — "sidik jari" versi resource — digunakan untuk conditional requests:
ETag: "abc123def456"Client bisa kirim If-None-Match: "abc123def456" di request berikutnya — server akan balas 304 Not Modified kalau belum berubah, sehingga menghemat bandwidth.
Set-Cookie — menyimpan cookie di browser:
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=86400Security Headers
Server sebaiknya mengirimkan headers keamanan untuk melindungi dari serangan umum:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'
Referrer-Policy: strict-origin-when-cross-originCORS: Kenapa Browser Memblokir Request?
Kalau kamu pernah lihat error merah di console browser yang bertuliskan "blocked by CORS policy", inilah biang keroknya. CORS (Cross-Origin Resource Sharing) adalah mekanisme keamanan browser yang mencegah JavaScript di app.backendlabs.id diam-diam mengakses api.otherdomain.com tanpa izin eksplisit.
"Origin" didefinisikan oleh kombinasi: scheme + host + port. Jadi:
https://backendlabs.iddanhttps://api.backendlabs.id→ berbeda originhttp://backendlabs.iddanhttps://backendlabs.id→ berbeda origin (scheme beda)https://backendlabs.id:3000danhttps://backendlabs.id:4000→ berbeda origin (port beda)
Preflight Request: Untuk request yang "tidak sederhana" — pakai method selain GET/POST, atau header custom — browser tidak langsung menembakkan requestnya. Ia lebih dulu mengirim OPTIONS secara otomatis, semacam bertanya izin dulu apakah cross-origin request ini boleh dilakukan:
OPTIONS /api/v1/products HTTP/1.1
Origin: https://backendlabs.id
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, AuthorizationServer perlu merespons dengan headers yang mengizinkan:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://backendlabs.id
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 ← cache preflight response 24 jamDi Node.js/Express, cukup pakai package cors:
import cors from 'cors';
app.use(cors({
origin: ['https://backendlabs.id', 'https://www.backendlabs.id'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // izinkan cookies/auth header
}));Penting: CORS dijalankan oleh browser — bukan server. Tools seperti
curldan Postman tidak mengikuti CORS, sehingga request mereka selalu berhasil tanpa peduli header CORS. CORS hanya relevan ketika JavaScript di browser yang mengirimkan request.
Bab 6: JSON: Format Data di Web Modern
Mengapa JSON?
Saya masih sempat berurusan dengan XML di awal-awal karir, dan rasanya seperti menulis surat resmi hanya untuk mengirim data yang sebetulnya sederhana. Jadi ketika JSON mulai populer, hampir semua orang pindah tanpa menoleh lagi — dan menurut saya itu keputusan yang masuk akal. Sebelum JSON populer, XML adalah format standar. Tapi XML verbose dan susah di-parse. JSON (JavaScript Object Notation) menggantinya karena:
- Ringkas dan mudah dibaca manusia
- Natively didukung JavaScript (
JSON.parse,JSON.stringify) - Library tersedia di hampir semua bahasa pemrograman
- Ukuran payload lebih kecil dari XML untuk data yang sama
Sintaks JSON
JSON hanya punya 6 tipe data:
{
"string": "Hello, World!",
"number": 42,
"float": 3.14159,
"boolean": true,
"null_val": null,
"array": [1, "dua", true, null],
"object": {
"nested_key": "nested_value"
}
}Aturan yang sering dilupakan:
- Key harus pakai double quote
"key"— single quote'key'tidak valid - String harus pakai double quote
- Tidak boleh trailing comma:
[1, 2, 3,]← tidak valid JSON (tapi valid JavaScript!) - Tidak ada komentar (
//atau/* */) dalam JSON murni - Angka tidak boleh diawali nol:
0123← tidak valid
JSON di JavaScript
// Parse JSON string menjadi JavaScript object
const jsonString = '{"name":"Rinaldy","age":28}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "Rinaldy"
// Konversi JavaScript object menjadi JSON string
const data = { name: "Rinaldy", skills: ["Node.js", "PostgreSQL"] };
const json = JSON.stringify(data);
// '{"name":"Rinaldy","skills":["Node.js","PostgreSQL"]}'
// Dengan indentasi untuk debugging
const prettyJson = JSON.stringify(data, null, 2);
/*
{
"name": "Rinaldy",
"skills": [
"Node.js",
"PostgreSQL"
]
}
*/Pitfall: JSON.parse Bisa Throw Error
Ini pitfall yang saya pernah kena sendiri waktu masih awal-awal: JSON.parse bisa melempar error kalau inputnya tidak valid, jadi selalu bungkus dengan try/catch kalau sumber datanya tidak terjamin bersih:
// ✗ Berbahaya
const data = JSON.parse(req.body); // crash jika body bukan JSON valid
// ✓ Aman
try {
const data = JSON.parse(req.body);
} catch (err) {
return res.status(400).json({ error: 'Invalid JSON' });
}
// Lebih baik: pakai middleware Express yang otomatis handle ini
app.use(express.json()); // sudah ada error handling bawaanDate & Time di JSON
JSON sebenarnya tidak punya tipe data Date sama sekali — jadi konvensinya, semua timestamp dikirim sebagai string dengan format ISO 8601:
{
"createdAt": "2026-06-29T10:30:00.000Z",
"expiresAt": "2026-07-06T10:30:00.000Z"
}Huruf Z di akhir itu penanda UTC (Coordinated Universal Time), dan ini bukan detail sepele — kebiasaan yang saya pegang teguh: selalu simpan dan kirim timestamp dalam UTC, biar frontend yang urus konversi ke timezone lokal user.
// Buat timestamp UTC
new Date().toISOString() // "2026-06-29T10:30:00.000Z"
// Parse ISO string
new Date("2026-06-29T10:30:00.000Z")API Response Pattern yang Konsisten
Satu kebiasaan yang saya pegang di setiap project: pastikan struktur response API konsisten, apapun endpoint-nya. Ini memudahkan frontend memproses apapun yang dikembalikan tanpa perlu penanganan khusus tiap kali:
// ✓ Single resource
{
"success": true,
"data": {
"id": 42,
"name": "Ebook Docker"
}
}
// ✓ Collection dengan pagination
{
"success": true,
"data": [
{ "id": 1, "name": "Ebook HTTP" },
{ "id": 2, "name": "Ebook Git" }
],
"meta": {
"total": 23,
"page": 1,
"perPage": 10,
"totalPages": 3
}
}
// ✓ Error
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Produk dengan ID 42 tidak ditemukan"
}
}Bab 7: REST: Konvensi Membangun API
Apa Itu REST?
REST sering disalahpahami sebagai teknologi atau protokol tertentu, padahal bukan. REST (Representational State Transfer) itu lebih tepat disebut arsitektur gaya (architectural style) — kumpulan konvensi untuk membangun API di atas HTTP, bukan aturan baku yang wajib diikuti persis.
API yang mengikuti konvensi ini disebut RESTful API, dan hampir semua API publik yang kamu kenal — GitHub, Stripe, Twitter — mengikutinya.
Prinsip Utama REST
1. Resources, bukan aksi
URL mewakili benda (resource), bukan aksi. Method HTTP yang menunjukkan aksi.
// ✗ Bukan REST — URL berisi aksi
GET /getProducts
POST /createProduct
POST /deleteProduct/42
// ✓ REST — URL adalah resource, method adalah aksi
GET /products ← ambil semua produk
POST /products ← buat produk baru
GET /products/42 ← ambil produk #42
PUT /products/42 ← ganti produk #42
PATCH /products/42 ← update sebagian produk #42
DELETE /products/42 ← hapus produk #42
2. Gunakan noun jamak
Konvensinya, pakai bentuk jamak yang konsisten untuk resource collection — kedengarannya sepele, tapi begitu tim mulai campur aduk singular/jamak, API jadi tidak enak dipakai:
/products ← bukan /product
/users ← bukan /user
/categories ← bukan /category
3. Nested resources untuk relasi
GET /users/5/orders ← semua order milik user #5
GET /users/5/orders/12 ← order #12 milik user #5
POST /products/42/reviews ← buat review untuk produk #42
Hindari nesting lebih dari 2 level — dari pengalaman saya, begitu lebih dari itu, URL-nya jadi panjang dan justru menyulitkan siapapun yang memakainya.
4. Query string untuk filter, sort, pagination
GET /products?category=backend ← filter
GET /products?sort=price&order=asc ← sort
GET /products?page=2&limit=10 ← pagination
GET /products?q=docker ← search
GET /products?category=backend&sort=price&page=1&limit=10 ← kombinasi
Versioning API
Cepat atau lambat, API-mu akan berubah. Tanpa versioning, perubahan itu bisa merusak client yang sudah terlanjur bergantung pada versi lama. Cara paling umum untuk menghindarinya: taruh versi di URL.
GET /api/v1/products ← version 1
GET /api/v2/products ← version 2 (mungkin ada perubahan response format)
Alternatif yang lebih "pure REST": versi di header:
Accept: application/vnd.backendlabs.v2+jsonTapi versi di URL lebih mudah di-debug dan lebih umum dipakai.
Idempotency Key untuk POST
Ingat POST tidak idempotent? Ini konsekuensinya yang paling nyata: request yang sama, dikirim dua kali, bisa menghasilkan dua resource yang seharusnya cuma satu. Untuk operasi yang taruhannya tinggi — pembayaran, pengiriman email — solusinya pakai idempotency key:
POST /api/v1/payments HTTP/1.1
Idempotency-Key: uuid-abc-123-def-456
{ "amount": 99000, "product_id": 42 }Server menyimpan key ini dan jika menerima request dengan key yang sama, mengembalikan response yang sama tanpa memproses ulang. Ini mencegah double-charge jika client mengirim request dua kali karena network timeout.
Membaca Dokumentasi API
Skill yang jarang diajarkan eksplisit tapi pasti kamu butuh: membaca dokumentasi API orang lain. Hampir semua API publik punya dokumentasi, dan mengenali format yang umum akan sangat membantu:
Format OpenAPI/Swagger:
GET /products/{id}:
summary: Get product by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: Product not foundKetika membaca docs API, perhatikan:
- Base URL — prefix semua endpoint, misal
https://api.stripe.com/v1 - Authentication — cara autentikasi (API key di header, Bearer token, dll)
- Rate limits — berapa request per detik/menit yang diizinkan
- Response format — struktur JSON yang dikembalikan
- Error codes — error code spesifik yang bisa dikembalikan API tersebut
Bab 8: Coba Sendiri: curl & Hoppscotch
curl — HTTP Client di Terminal
curl adalah tool command-line andalan saya setiap kali perlu tes API cepat tanpa buka aplikasi lain — dan enaknya, sudah terinstall di Mac dan Linux, tinggal pakai.
GET request:
# Request sederhana
curl https://api.github.com/users/torvalds
# Dengan formatting JSON (butuh python3)
curl https://api.github.com/users/torvalds | python3 -m json.tool
# Hanya tampilkan status code
curl -s -o /dev/null -w "%{http_code}" https://backendlabs.id/api/healthPOST dengan JSON body:
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"title":"Belajar HTTP","body":"Ini isi postnya"}' \
https://jsonplaceholder.typicode.com/postsFlag-flag penting:
-X METHOD # specify HTTP method: -X POST, -X DELETE, dsb
-H "Key: Val" # tambahkan header
-d 'data' # request body (otomatis set -X POST)
-s # silent mode: sembunyikan progress bar
-v # verbose: tampilkan request + response headers
-i # include response headers di output
-o file.json # simpan response ke file
-L # ikuti redirect
--max-time 10 # timeout setelah 10 detikLihat full request dan response:
curl -sv https://api.github.com/users/torvalds 2>&1 | head -60
# Prefix > = request yang dikirim
# Prefix < = response yang diterimaUpload file:
curl -X POST \
-H "Authorization: Bearer TOKEN" \
-F "file=@/path/to/foto.jpg" \
-F "description=Profile photo" \
https://api.example.com/uploadAPI Publik untuk Latihan
Kalau kamu ingin coba-coba tanpa perlu daftar akun ke manapun, API publik ini bisa jadi sasaran latihan:
# JSONPlaceholder — fake REST API untuk testing
curl https://jsonplaceholder.typicode.com/posts/1
curl https://jsonplaceholder.typicode.com/users
curl -X POST -H "Content-Type: application/json" \
-d '{"title":"test","body":"isi","userId":1}' \
https://jsonplaceholder.typicode.com/posts
# Open Meteo — cuaca tanpa API key
curl "https://api.open-meteo.com/v1/forecast?latitude=-6.2&longitude=106.8¤t=temperature_2m"
# GitHub API — data public repository
curl https://api.github.com/repos/expressjs/express
curl https://api.github.com/repos/expressjs/express/releases/latest | python3 -m json.tool
# IP Info
curl https://ipapi.co/json/Hoppscotch — GUI untuk HTTP Request
Kalau curl terasa terlalu manual, Hoppscotch bisa jadi alternatif — semacam Postman versi open-source yang langsung jalan di browser tanpa perlu install apapun. Beberapa fitur yang paling sering saya pakai:
Kirim request:
- Buka hoppscotch.io
- Pilih method (GET, POST, dll) dari dropdown
- Masukkan URL
- Tambahkan headers di tab "Headers"
- Tambahkan body JSON di tab "Body" → pilih "JSON"
- Klik "Send"
Simpan dan organisir requests:
- Gunakan Collections untuk mengelompokkan request berdasarkan project
- Bisa export collection sebagai JSON dan dibagikan ke tim
Environment Variables:
base_url = https://api.backendlabs.id
token = eyJhbGci...
Lalu gunakan di URL: {{base_url}}/products dan di header: Authorization: Bearer {{token}}
Latihan Terstruktur
Latihan 1 — GET:
Method: GET
URL: https://jsonplaceholder.typicode.com/todos?userId=1&completed=false
Perhatikan: query string untuk filter, response array of objects, status 200.
Latihan 2 — POST:
Method: POST
URL: https://jsonplaceholder.typicode.com/posts
Headers: Content-Type: application/json
Body:
{
"title": "Ebook BackendLabs",
"body": "Saya sedang belajar HTTP",
"userId": 1
}
Perhatikan: response 201 Created, server assign id secara otomatis.
Latihan 3 — 404:
Method: GET
URL: https://jsonplaceholder.typicode.com/posts/99999
Perhatikan: response 404 Not Found, body {}.
Latihan 4 — Error parsing: Coba kirim JSON yang tidak valid (tambahkan koma di akhir):
{ "name": "test", }
Perhatikan status 400 atau 422 tergantung implementasi server.
Penutup
Mental model yang kamu bangun di artikel ini akan terus kamu pakai seumur karir sebagai backend developer. Setiap kali kamu debug kenapa API tidak merespons, kenapa CORS error, atau kenapa request gagal — kamu akan kembali ke dasar-dasar ini.