Web Artisan
Beranda

Progress belajar

Modul 13 dari 73

0% 0/73 modul selesai

Setelah selesai, tandai modul ini agar progres kursus tetap rapi.

Progress disimpan lokal di browser ini.

Roadmap 2 · Web API

Fundamental HTTP
untuk Backend Developer

HTTP adalah kontrak komunikasi antara React frontend, Go API, dan sistem lain seperti payment gateway.

Bahasa: Go 1.26Roadmap 2~65 menit baca
01

Kenapa HTTP Harus Dikuasai Dulu

Sebelum bicara router, middleware, handler, dan framework, backend developer harus paham bahasa yang dipakai client dan server untuk saling bicara.

Kalau kamu datang dari React, kamu mungkin sering menulis fetch, axios.get, atau mutation di React Query. Di sisi frontend, request terlihat seperti function call ke URL. Di sisi backend Go, request itu bukan function call biasa. Ia adalah pesan HTTP yang membawa method, path, headers, query string, body, dan informasi koneksi.

Kalau kamu datang dari Laravel, kamu sudah mengenal route seperti Route::get('/products/{id}', ...), request object, response JSON, dan middleware. Go juga punya konsep yang sama, tetapi lebih dekat ke protokol dasarnya. Package standar net/http langsung memberi http.Request, http.ResponseWriter, http.Handler, dan server HTTP tanpa framework besar.

🌉Jembatan: dari fetch ke handler Go

Di React, fetch('/v1/products') mengirim pesan HTTP. Di Go, handler menerima pesan itu sebagai *http.Request, lalu menulis balasan lewat http.ResponseWriter.

Pada proyek online shop skincare, HTTP menjadi kontrak publik untuk katalog produk, cart, checkout, order history, login, upload bukti pembayaran, dan payment webhook. Jika kontraknya kabur, frontend akan sulit dipakai, error handling berantakan, dan integrasi payment menjadi rapuh.

HTTP

HTTP adalah protokol request-response. Client mengirim request berisi niat dan data. Server memprosesnya, lalu mengembalikan response berisi status, metadata, dan body opsional.

02

Request dan Response

Setiap interaksi API dimulai dari request dan selesai dengan response.

Request adalah pesan dari client ke server. Client bisa berupa browser, React app, mobile app, cURL, Postman, worker internal, atau payment gateway. Response adalah balasan server yang memberi tahu hasilnya, sukses, gagal karena input, gagal karena hak akses, atau gagal karena server.

HTTP request sederhana
GET /v1/products?skin_type=oily&page=1 HTTP/1.1 Host: api.skincare.local Accept: application/json Authorization: Bearer eyJhbGciOi... X-Request-ID: req_7HcP0W
HTTP response sederhana
HTTP/1.1 200 OK Content-Type: application/json X-Request-ID: req_7HcP0W { "data": [ { "id": "prd_001", "name": "Niacinamide Serum", "price": 129000 } ], "meta": { "page": 1, "page_size": 20 } }

Di Go, request dibaca dari *http.Request. Path ada di r.URL.Path, method ada di r.Method, query ada di r.URL.Query(), header ada di r.Header, dan body dibaca dari r.Body. Response ditulis ke http.ResponseWriter, biasanya dengan status code, header, lalu JSON.

React / JS
  • fetch menyusun request dan menerima response sebagai object yang bisa dibaca.
  • Error jaringan dan HTTP error perlu dibedakan sendiri oleh frontend.
Go Backend
  • Handler membaca request dari *http.Request dan menulis response secara eksplisit.
  • Status code harus dipilih dengan sengaja agar frontend tahu langkah berikutnya.
💡Prinsip desain API

Anggap request dan response sebagai kontrak produk. React, mobile, worker, dan payment gateway akan bergantung pada kontrak ini.

03

HTTP Methods dan Semantiknya

HTTP method menjelaskan niat request, bukan sekadar nama teknis.

Di frontend, semua bisa terlihat seperti memanggil URL. Di backend, method adalah bagian penting dari kontrak. GET /v1/products dan POST /v1/products boleh memakai path yang mirip, tetapi artinya berbeda total.

GET

Mengambil data tanpa mengubah state server, misalnya daftar produk atau detail order.

POST

Membuat resource atau menjalankan aksi yang tidak idempotent, misalnya checkout atau menerima webhook.

PUT

Mengganti resource secara utuh dan idempotent, cocok untuk update penuh alamat pengiriman.

PATCH

Mengubah sebagian field, misalnya update quantity satu item cart.

DELETE

Menghapus resource atau membatalkan hubungan, misalnya hapus item dari cart.

OPTIONS

Umumnya dipakai browser untuk CORS preflight sebelum request tertentu.

idempotent

Operasi idempotent memberi hasil akhir yang sama walau request yang sama dikirim berulang. PUT biasanya idempotent, POST /checkout biasanya tidak.

Contoh semantik pada skincare API:

Contoh method dan niat
GET /v1/products # baca daftar produk GET /v1/products/prd_001 # baca detail produk POST /v1/cart/items # tambah item baru ke cart PATCH /v1/cart/items/item_123 # ubah quantity item cart DELETE /v1/cart/items/item_123 # hapus item dari cart POST /v1/orders # buat order dari cart POST /v1/payment/webhooks/xendit # terima event dari payment gateway
⚠️Jebakan: semua aksi memakai POST

POST untuk semua endpoint memang cepat di awal, tetapi kontrak API jadi sulit dipahami, cache sulit, observability kabur, dan frontend tidak punya sinyal semantik yang jelas.

04

Status Code yang Wajib Hafal

Status code adalah sinyal ringkas untuk client, log, monitoring, dan manusia yang membaca response.

Kamu tidak perlu menghafal semua status code. Untuk backend API sehari-hari, ada sekumpulan kecil yang harus konsisten dipakai. Konsistensi lebih penting daripada variasi yang terlalu kreatif.

200 OK

Request berhasil dan response berisi data, misalnya daftar produk atau detail order.

201 Created

Resource berhasil dibuat, misalnya item cart, order, atau alamat pengiriman baru.

400 Bad Request

Request tidak valid secara umum, misalnya JSON rusak atau query parameter tidak bisa diparse.

401 Unauthorized

Client belum terautentikasi atau token tidak valid.

403 Forbidden

Client sudah dikenal, tetapi tidak punya izin melakukan aksi itu.

404 Not Found

Resource tidak ditemukan, misalnya product ID tidak ada.

409 Conflict

Request bentrok dengan state saat ini, misalnya stok habis saat checkout.

422 Unprocessable Content

JSON valid, tetapi aturan bisnis atau validasi field gagal.

500 Internal Server Error

Kesalahan tidak terduga di server. Jangan bocorkan detail internal ke client.

Diagram berikut membantu memilih status code yang tepat untuk setiap kondisi di Go handler:

flowchart TD
  REQ([Request masuk]) --> AUTH{Autentikasi valid}
  AUTH -->|tidak| C401[401 Unauthorized]
  AUTH -->|ya| PERM{Punya izin}
  PERM -->|tidak| C403[403 Forbidden]
  PERM -->|ya| FOUND{Resource ada}
  FOUND -->|tidak| C404[404 Not Found]
  FOUND -->|ya| VALID{Input valid}
  VALID -->|JSON rusak| C400[400 Bad Request]
  VALID -->|aturan bisnis gagal| C422[422 Unprocessable]
  VALID -->|ok| EXEC{Eksekusi berhasil}
  EXEC -->|konflik state| C409[409 Conflict]
  EXEC -->|error tak terduga| C500[500 Server Error]
  EXEC -->|resource dibuat| C201[201 Created]
  EXEC -->|data dikembalikan| C200[200 OK]

Gambar 1. Pohon keputusan pemilihan status code HTTP di setiap Go handler.

Laravel / PHP
  • Framework sering menyediakan helper seperti response()->json($data, 201).
  • Exception handler bisa otomatis mengubah exception tertentu menjadi status code.
Go
  • Kamu memilih status code secara eksplisit lewat w.WriteHeader(status) atau helper milik proyek.
  • Error sebagai nilai membuat mapping error ke status code perlu dirancang sendiri.
🧭401 vs 403

401 berarti masalah autentikasi. 403 berarti identitas sudah diketahui, tetapi akses ditolak. Jangan membalik keduanya karena frontend biasanya bereaksi berbeda.

05

Headers: Metadata untuk API

Header adalah metadata request dan response. Ia tidak membawa data domain utama, tetapi sangat penting untuk parsing, keamanan, tracing, dan cache.

Header paling sering kamu sentuh di API backend adalah Content-Type, Authorization, dan X-Request-ID.

Content-Type

Header yang memberi tahu media type body. Untuk JSON API, request dan response biasanya memakai application/json.

Authorization

Header untuk membawa credential, misalnya Bearer token. Di Roadmap 7 kita akan menghubungkannya dengan JWT dan session security.

X-Request-ID

ID korelasi untuk menelusuri satu request dari client, API, log, database, sampai worker. Sangat berguna saat debugging produksi.

Access-Control-Allow-Origin

Header response yang mengizinkan browser dari origin tertentu membaca response. Diperlukan agar React di localhost:3000 bisa memanggil Go API di localhost:8080 tanpa diblokir browser. Konfigurasi CORS middleware dibahas di Roadmap 2 Chapter 5.

Header request checkout
POST /v1/orders HTTP/1.1 Host: api.skincare.local Content-Type: application/json Authorization: Bearer eyJhbGciOi... X-Request-ID: req_checkout_20260606_0001
Header response checkout
HTTP/1.1 201 Created Content-Type: application/json X-Request-ID: req_checkout_20260606_0001
⚠️Jangan simpan rahasia di query string

Token, API key, dan data sensitif jangan dikirim lewat query parameter karena lebih mudah muncul di log, browser history, reverse proxy, dan analytics.

Dalam Go, header adalah map khusus bernama http.Header. Ia case-insensitive secara semantik, tetapi tetap gunakan nama standar agar mudah dibaca. Untuk response JSON, set Content-Type sebelum menulis body.

internal/httpjson/response.go
package httpjson import ( "encoding/json" "net/http" ) func WriteJSON(w http.ResponseWriter, status int, payload any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(payload) }
06

Body, Query Parameter, dan Path Parameter

Tidak semua data request harus masuk ke tempat yang sama. Path, query, header, dan body punya peran berbeda.

Gunakan path parameter untuk identitas resource. Gunakan query parameter untuk filter, sort, search, dan pagination. Gunakan body untuk payload yang kompleks, terutama saat membuat atau mengubah data.

Express / React
  • Express sering menulis /products/:id dan membaca req.params.id.
  • React biasanya menyusun URL dengan template string atau library router.
Go dan chi nanti
  • chi menulis /products/{id} dan membaca dengan chi.URLParam(r, "id").
  • net/http murni bisa membaca path, tetapi routing param akan lebih nyaman dengan router.
Path, query, dan body
GET /v1/products/prd_001 # prd_001 adalah path parameter, identitas produk GET /v1/products?skin_type=oily&sort=price_asc&page=1 # skin_type, sort, dan page adalah query parameter POST /v1/cart/items # payload item dikirim di body JSON
request body POST /v1/cart/items
{ "product_id": "prd_001", "quantity": 2 }
Pilih path untuk resource

Gunakan noun yang stabil seperti /v1/products, /v1/cart/items, dan /v1/orders.

Pilih method untuk niat

GET membaca, POST membuat atau menjalankan aksi, PATCH mengubah sebagian, DELETE menghapus.

Pilih tempat data

ID di path, filter di query, payload domain di body, metadata teknis di header.

💡Praktik aman

Batasi ukuran body, validasi query parameter, dan jangan percaya path parameter sebelum dicek di service atau repository.

07

Dasar JSON API yang Konsisten

JSON API yang baik bukan hanya valid JSON, tetapi juga konsisten dalam bentuk response sukses dan gagal.

Di React, konsistensi response membuat hook lebih sederhana. Di backend Go, konsistensi response membuat handler lebih mudah dites, error lebih mudah dipetakan, dan dokumentasi API lebih mudah ditulis.

response sukses GET /v1/products/prd_001
{ "data": { "id": "prd_001", "name": "Niacinamide Serum", "slug": "niacinamide-serum", "price": 129000, "stock": 42, "skin_types": ["oily", "combination"] } }
response list dengan metadata
{ "data": [ { "id": "prd_001", "name": "Niacinamide Serum", "price": 129000 }, { "id": "prd_002", "name": "Gentle Cleanser", "price": 99000 } ], "meta": { "page": 1, "page_size": 20, "total": 84 } }
response error validasi
{ "error": { "code": "VALIDATION_ERROR", "message": "quantity must be at least 1", "fields": { "quantity": "must be greater than 0" } } }
🌉Jembatan: dari TypeScript type ke kontrak JSON

Anggap response JSON sebagai type publik yang dipakai frontend. Bedanya, Go tidak tahu type TypeScript kamu, jadi kontrak harus dijaga lewat DTO, test, dan dokumentasi.

Pada modul berikutnya, kita akan mulai menulis helper writeJSON dan readJSON agar semua handler memakai format yang sama. Jangan biarkan tiap handler menulis bentuk error sendiri-sendiri.

08

Endpoint Utama Skincare API

Berikut peta awal endpoint yang akan menjadi tulang punggung online shop skincare kita.

GET /v1/health Health check untuk load balancer, deployment, dan monitoring
GET /v1/products Daftar produk dengan filter skin type, concern, harga, dan pagination
GET /v1/products/{id} Detail produk, termasuk harga, stok ringkas, ingredients, dan rekomendasi pemakaian
POST /v1/cart/items Menambahkan produk ke cart customer
PATCH /v1/cart/items/{id} Mengubah quantity item cart
DELETE /v1/cart/items/{id} Menghapus item dari cart
POST /v1/orders Membuat order dari cart dan melakukan reservasi stok
GET /v1/orders/{id} Melihat detail order milik customer
POST /v1/payment/webhooks/xendit Menerima callback status pembayaran dari payment gateway
📝Tentang versi API

Prefix /v1 membuat kontrak lebih aman saat nanti ada perubahan besar. Versi API bukan izin untuk sering breaking change, tetapi pagar untuk evolusi jangka panjang.

Endpoint di atas belum membahas autentikasi, otorisasi, transaksi database, dan validasi penuh. Itu akan datang bertahap. Tujuan chapter ini adalah membaca endpoint sebagai kontrak HTTP yang jelas.

09

Alur Request dari React ke PostgreSQL

Satu request API yang terlihat sederhana biasanya melewati beberapa lapisan sebelum menghasilkan response.

sequenceDiagram
  autonumber
  participant FE as React Frontend
  participant API as Go API
  participant MW as Middleware
  participant H as Handler
  participant S as Service
  participant R as Repository pgx
  participant DB as PostgreSQL
  FE->>API: POST /v1/orders JSON
  API->>MW: request masuk
  MW->>MW: request id, logging, auth
  MW->>H: teruskan request
  H->>H: decode JSON dan validasi bentuk awal
  H->>S: CreateOrder(ctx, input)
  S->>S: cek aturan bisnis checkout
  S->>R: reserve stock dan insert order
  R->>DB: SQL transaction
  DB-->>R: rows affected dan order id
  R-->>S: result atau error
  S-->>H: order baru
  H-->>FE: 201 Created JSON

Gambar 2. Alur request checkout dari React frontend ke Go API sampai PostgreSQL.

Bagian pentingnya adalah setiap lapisan punya tanggung jawab berbeda. Handler memahami HTTP. Service memahami bisnis. Repository memahami database. HTTP status diputuskan di tepi API, tetapi penyebabnya biasanya berasal dari validasi, aturan bisnis, atau database.

💡Mental model backend

Jangan taruh semua logika di handler. Handler sebaiknya menerjemahkan HTTP menjadi input service, lalu menerjemahkan hasil service menjadi HTTP response.

10

Hands-on Mini API dengan net/http

Kita mulai dari net/http agar kamu melihat bentuk asli HTTP server Go sebelum chi masuk di chapter berikutnya.

Sejak Go 1.22, http.ServeMux mendukung pattern method dan path secara langsung. Ini cukup untuk contoh kecil. Pada proyek besar, kita akan memakai chi karena grouping route, middleware chain, dan path parameter lebih nyaman.

Struktur mini project
  • cmd/
  • api/
  • main.go server HTTP, handler, dan middleware
  • go.mod module: github.com/kamu/skincare-backend
cmd/api/main.go
package main import ( "encoding/json" "fmt" "log/slog" "net/http" "time" ) type productResponse struct { ID string `json:"id"` Name string `json:"name"` PriceRupiah int64 `json:"price"` } func main() { mux := http.NewServeMux() mux.HandleFunc("GET /v1/health", healthHandler) mux.HandleFunc("GET /v1/products", listProductsHandler) srv := &http.Server{ Addr: ":8080", Handler: requestID(mux), ReadHeaderTimeout: 5 * time.Second, } slog.Info("api server listening", "addr", srv.Addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { slog.Error("server stopped", "error", err) } } func healthHandler(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func listProductsHandler(w http.ResponseWriter, r *http.Request) { skinType := r.URL.Query().Get("skin_type") _ = skinType products := []productResponse{ {ID: "prd_001", Name: "Niacinamide Serum", PriceRupiah: 129000}, {ID: "prd_002", Name: "Gentle Cleanser", PriceRupiah: 99000}, } writeJSON(w, http.StatusOK, map[string]any{ "data": products, "meta": map[string]any{"page": 1, "page_size": 20}, }) } func writeJSON(w http.ResponseWriter, status int, payload any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(payload); err != nil { slog.Error("encode response", "error", err) } } func requestID(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := r.Header.Get("X-Request-ID") if id == "" { id = fmt.Sprintf("req_%x", time.Now().UnixNano()) } w.Header().Set("X-Request-ID", id) next.ServeHTTP(w, r) }) }
Terminal
go mod init github.com/kamu/skincare-backend go run ./cmd/api
Terminal
curl -i http://localhost:8080/v1/health curl -i "http://localhost:8080/v1/products?skin_type=oily&page=1"
Jalankan server

Gunakan go run ./cmd/api, lalu pastikan server mendengar di port 8080.

Panggil health check

Perhatikan status 200, header Content-Type, dan body JSON.

Panggil daftar produk

Tambahkan query parameter seperti skin_type=oily dan lihat bagaimana Go membacanya lewat r.URL.Query().

⚠️Catatan produksi

Contoh di atas sengaja minimal. Di produksi, request ID harus benar-benar unik (misalnya UUID), log harus membawa request ID di setiap baris, dan graceful shutdown wajib ditambahkan.

11

Jebakan Umum dari JS dan PHP

HTTP terlihat sederhana, tetapi banyak bug backend lahir dari salah menafsirkan detail kecil.

Menganggap 200 untuk semua hal

Frontend kehilangan sinyal. Validasi gagal sebaiknya bukan 200, gunakan 400 atau 422 sesuai konteks.

Mencampur auth dan permission

Token hilang atau invalid adalah 401. User valid tetapi tidak boleh akses resource adalah 403.

Menaruh filter kompleks di path

Path untuk identitas resource. Filter dan pagination lebih cocok di query parameter.

Body GET sebagai kebiasaan

Jangan desain GET yang butuh body. Banyak client, proxy, dan tooling tidak mengandalkannya.

Response error tidak konsisten

React akan penuh kondisi khusus jika setiap endpoint mengirim bentuk error yang berbeda.

Membocorkan error internal

Jangan kirim pesan database mentah ke client. Simpan detail di log, kirim pesan aman ke response.

🌉Jembatan: HTTP error bukan exception

Di JS, fetch tidak otomatis throw untuk status 404 atau 500. Di Go, kamu juga tidak throw. Dua sisi harus sepakat membaca status code dan body error.

🧩Kapan pakai 400 atau 422

Gunakan 400 untuk request yang bentuknya tidak bisa dipahami, seperti JSON rusak. Gunakan 422 untuk payload yang bisa dibaca tetapi gagal aturan validasi, seperti quantity 0.

12

Ringkasan & Poin Penting

HTTP adalah fondasi semua chapter di Roadmap 2, dari handler net/http, routing chi, middleware, validasi, sampai testing API.

Yang Wajib Menempel

  • Request membawa method, path, query, headers, dan body. Response membawa status code, headers, dan body.
  • HTTP method menjelaskan niat. GET membaca, POST membuat atau menjalankan aksi, PATCH mengubah sebagian, DELETE menghapus.
  • Status code adalah kontrak. Gunakan 200, 201, 400, 401, 403, 404, 409, 422, dan 500 secara konsisten.
  • Content-Type, Authorization, X-Request-ID, dan Access-Control-Allow-Origin adalah header yang wajib akrab untuk API produksi.
  • Path parameter untuk identitas, query parameter untuk filter, body untuk payload domain, header untuk metadata teknis.
  • JSON response harus konsisten agar frontend React, test, logging, dan dokumentasi API mudah dijaga.
  • Dalam proyek skincare, endpoint produk, cart, order, dan payment webhook akan dibangun dari kontrak HTTP ini.

Langkah berikutnya adalah masuk ke net/http lebih serius. Kita akan membedah http.Handler, http.ResponseWriter, *http.Request, helper JSON, timeout server, dan cara menyusun handler yang siap dipindahkan ke chi.

Progress disimpan lokal di browser ini.