Lewati ke konten utama
Semua artikel
PemulaFondasi

Cara Kerja HTTP & Internet

Pahami request/response, HTTP methods, status codes, headers, dan JSON sebelum membangun API pertamamu.

25 menit baca
Bagikan

Instagram & WhatsApp Story tidak punya share-link langsung — unduh gambarnya, lalu unggah manual sebagai Story.


"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

  1. Bagaimana Internet Bekerja
  2. HTTP: Bahasa Komputer di Internet
  3. HTTP Methods: Kata Kerja di API
  4. Status Codes yang Harus Kamu Hafal
  5. Headers: Metadata di Setiap Request
  6. JSON: Format Data di Web Modern
  7. REST: Konvensi Membangun API
  8. 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.8

Langkah 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 protokol
  • Host sampai Accept — 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 kategori

Karakteristik 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 + header Location yang 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.1

Setelah 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.

MethodBodySafeIdempotentResponse 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.

CodeTextKapan dipakai
200OKRequest berhasil — respons standar untuk GET, PUT, PATCH
201CreatedResource baru berhasil dibuat (POST)
202AcceptedRequest diterima tapi belum selesai diproses (async job)
204No ContentBerhasil, tidak ada data yang dikembalikan (DELETE, PATCH tanpa body)

3xx — Redirect

Kategori ini bukan berarti gagal, cuma perlu mampir dulu ke URL lain.

CodeTextKapan dipakai
301Moved PermanentlyURL sudah pindah permanen — browser akan update bookmark
302FoundRedirect sementara — browser tidak update bookmark
304Not ModifiedResource 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.

CodeTextKapan dipakai
400Bad RequestRequest tidak valid: format JSON salah, field kurang/salah tipe
401UnauthorizedBelum autentikasi — token tidak ada, expired, atau invalid
403ForbiddenSudah autentikasi tapi tidak punya izin untuk resource ini
404Not FoundResource tidak ditemukan di server
405Method Not AllowedMethod tidak didukung untuk endpoint ini
409ConflictKonflik data: email sudah terdaftar, duplicate entry
410GoneResource pernah ada tapi sudah dihapus permanen
422Unprocessable EntityData bisa di-parse tapi gagal validasi bisnis
429Too Many RequestsRate 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.

CodeTextKapan dipakai
500Internal Server ErrorError tak terduga — bug di kode, exception tidak tertangani
502Bad GatewayServer upstream (database, microservice) tidak merespons
503Service UnavailableServer kelebihan beban atau sedang maintenance
504Gateway TimeoutUpstream 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-urlencoded

Authorization — token autentikasi:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Authorization: Basic dXNlcjpwYXNzd29yZA==   ← Base64(user:password)
Authorization: ApiKey sk-abc123xyz

Accept — format response yang diinginkan:

Accept: application/json
Accept: text/html, application/xhtml+xml, */*

Content-Length — ukuran body dalam bytes:

Content-Length: 156

Accept-Language — preferensi bahasa:

Accept-Language: id-ID, id;q=0.9, en;q=0.8

Headers 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/png

Location — URL resource yang baru dibuat (bersama 201 Created):

HTTP/1.1 201 Created
Location: /api/v1/products/43

Cache-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 jam

ETag — "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=86400

Security 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-origin

CORS: 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.id dan https://api.backendlabs.id → berbeda origin
  • http://backendlabs.id dan https://backendlabs.id → berbeda origin (scheme beda)
  • https://backendlabs.id:3000 dan https://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, Authorization

Server 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 jam

Di 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 curl dan 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 bawaan

Date & 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+json

Tapi 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 found

Ketika membaca docs API, perhatikan:

  1. Base URL — prefix semua endpoint, misal https://api.stripe.com/v1
  2. Authentication — cara autentikasi (API key di header, Bearer token, dll)
  3. Rate limits — berapa request per detik/menit yang diizinkan
  4. Response format — struktur JSON yang dikembalikan
  5. 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/health

POST 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/posts

Flag-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 detik

Lihat full request dan response:

curl -sv https://api.github.com/users/torvalds 2>&1 | head -60
# Prefix > = request yang dikirim
# Prefix < = response yang diterima

Upload file:

curl -X POST \
     -H "Authorization: Bearer TOKEN" \
     -F "file=@/path/to/foto.jpg" \
     -F "description=Profile photo" \
     https://api.example.com/upload

API 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&current=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:

  1. Buka hoppscotch.io
  2. Pilih method (GET, POST, dll) dari dropdown
  3. Masukkan URL
  4. Tambahkan headers di tab "Headers"
  5. Tambahkan body JSON di tab "Body" → pilih "JSON"
  6. 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.

Checklist Pemahaman