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.
Fundamental HTTP
untuk Backend Developer
HTTP adalah kontrak komunikasi antara React frontend, Go API, dan sistem lain seperti payment gateway.
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.
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 adalah protokol request-response. Client mengirim request berisi niat dan data. Server memprosesnya, lalu mengembalikan response berisi status, metadata, dan body opsional.
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 sederhanaGET /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 sederhanaHTTP/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.
fetchmenyusun request dan menerima response sebagai object yang bisa dibaca.- Error jaringan dan HTTP error perlu dibedakan sendiri oleh frontend.
- Handler membaca request dari
*http.Requestdan menulis response secara eksplisit. - Status code harus dipilih dengan sengaja agar frontend tahu langkah berikutnya.
Anggap request dan response sebagai kontrak produk. React, mobile, worker, dan payment gateway akan bergantung pada kontrak ini.
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.
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 niatGET /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
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.
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.
- Framework sering menyediakan helper seperti
response()->json($data, 201). - Exception handler bisa otomatis mengubah exception tertentu menjadi status code.
- 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 berarti masalah autentikasi. 403 berarti identitas sudah diketahui, tetapi akses ditolak. Jangan membalik keduanya karena frontend biasanya bereaksi berbeda.
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.
Header yang memberi tahu media type body. Untuk JSON API, request dan response biasanya memakai application/json.
Header untuk membawa credential, misalnya Bearer token. Di Roadmap 7 kita akan menghubungkannya dengan JWT dan session security.
ID korelasi untuk menelusuri satu request dari client, API, log, database, sampai worker. Sangat berguna saat debugging produksi.
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 checkoutPOST /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 checkoutHTTP/1.1 201 Created Content-Type: application/json X-Request-ID: req_checkout_20260606_0001
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.gopackage 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) }
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 sering menulis
/products/:iddan membacareq.params.id. - React biasanya menyusun URL dengan template string atau library router.
- chi menulis
/products/{id}dan membaca denganchi.URLParam(r, "id"). net/httpmurni bisa membaca path, tetapi routing param akan lebih nyaman dengan router.
Path, query, dan bodyGET /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 }
Gunakan noun yang stabil seperti /v1/products, /v1/cart/items, dan /v1/orders.
GET membaca, POST membuat atau menjalankan aksi, PATCH mengubah sebagian, DELETE menghapus.
ID di path, filter di query, payload domain di body, metadata teknis di header.
Batasi ukuran body, validasi query parameter, dan jangan percaya path parameter sebelum dicek di service atau repository.
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" } } }
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.
Endpoint Utama Skincare API
Berikut peta awal endpoint yang akan menjadi tulang punggung online shop skincare kita.
/v1/health Health check untuk load balancer, deployment, dan monitoring /v1/products Daftar produk dengan filter skin type, concern, harga, dan pagination /v1/products/{id} Detail produk, termasuk harga, stok ringkas, ingredients, dan rekomendasi pemakaian /v1/cart/items Menambahkan produk ke cart customer /v1/cart/items/{id} Mengubah quantity item cart /v1/cart/items/{id} Menghapus item dari cart /v1/orders Membuat order dari cart dan melakukan reservasi stok /v1/orders/{id} Melihat detail order milik customer /v1/payment/webhooks/xendit Menerima callback status pembayaran dari payment gateway 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.
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.
Jangan taruh semua logika di handler. Handler sebaiknya menerjemahkan HTTP menjadi input service, lalu menerjemahkan hasil service menjadi HTTP response.
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.
- cmd/
- api/
- main.go server HTTP, handler, dan middleware
- go.mod module: github.com/kamu/skincare-backend
cmd/api/main.gopackage 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) }) }
Terminalgo mod init github.com/kamu/skincare-backend go run ./cmd/api
Terminalcurl -i http://localhost:8080/v1/health curl -i "http://localhost:8080/v1/products?skin_type=oily&page=1"
Gunakan go run ./cmd/api, lalu pastikan server mendengar di port 8080.
Perhatikan status 200, header Content-Type, dan body JSON.
Tambahkan query parameter seperti skin_type=oily dan lihat bagaimana Go membacanya lewat r.URL.Query().
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.
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.
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.
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.
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.
GETmembaca,POSTmembuat atau menjalankan aksi,PATCHmengubah sebagian,DELETEmenghapus. - Status code adalah kontrak. Gunakan
200,201,400,401,403,404,409,422, dan500secara konsisten. Content-Type,Authorization,X-Request-ID, danAccess-Control-Allow-Originadalah 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.