Episode ini membawa kalian melampaui REST: komunikasi realtime dengan WebSocket via gorilla/websocket, Server-Sent Events untuk streaming satu arah, serta mounting handler non-REST seperti gRPC-gateway dan GraphQL. Kalian juga membangun service campuran dalam satu http.Server.

REST menangani request-response, tapi banyak kebutuhan hidup di luarnya: notifikasi push, chat realtime, progress bar streaming, dan layanan RPC. Episode 16 menunjukkan bagaimana chi tetap relevan di dunia itu — karena router apa pun yang mengimplementasikan http.Handler bisa ditempelkan di path mana pun.
WebSocket, SSE, gRPC-gateway, dan GraphQL semuanya bermuara pada HTTP. Dengan chi, kalian bisa menggabungkan semuanya dalam satu server tanpa konflik.
Install dan bangun endpoint WebSocket:
go get github.com/gorilla/websocketvar upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(req *http.Request) bool {
return true
},
}
r.Get("/ws", func(w http.ResponseWriter, req *http.Request) {
conn, err := upgrader.Upgrade(w, req, nil)
if err != nil {
return
}
defer conn.Close()
for {
_, msg, err := conn.ReadMessage()
if err != nil {
return
}
conn.WriteMessage(websocket.TextMessage, msg)
}
})upgrader.Upgrade(w, req, nil) mengubah koneksi HTTP menjadi WebSocket. Loop membaca dan menulis pesan — server echo sederhana yang siap dikembangkan menjadi chat.
CheckOrigin yang mengembalikan true memudahkan development, tapi di production harus diverifikasi:
CheckOrigin: func(req *http.Request) bool {
origin := req.Header.Get("Origin")
return origin == "https://app.example.com"
}req.Header.Get("Origin") diperiksa terhadap allowlist — mencegah situs lain membuka koneksi WebSocket ke server kalian.
SSE mengalirkan data dari server ke klien lewat HTTP biasa:
r.Get("/events", func(w http.ResponseWriter, req *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming tidak didukung",
http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fmt.Fprintf(w, "data: ping\n\n")
flusher.Flush()
case <-req.Context().Done():
return
}
}
})fmt.Fprintf(w, "data: ping\n\n") menulis event dalam format SSE. <-req.Context().Done() memberhentikan streaming saat klien pergi — persis pola episode 12.
Jika kalian hanya butuh push notifikasi, SSE lebih sederhana daripada WebSocket.
gRPC-gateway menjembatani protobuf ke REST/HTTP. Hasil build-nya berupa http.Handler — tinggal di-mount:
gw, err := gateway.NewGateway(ctx)
r.Mount("/v1", gw)r.Mount("/v1", gw) menempelkan gateway yang mengembalikan http.Handler. Klien REST memanggil endpoint gRPC melalui path /v1/... tanpa tahu protokol di baliknya.
Server GraphQL juga hanyalah http.Handler:
srv := handler.NewDefaultServer(graphqlSchema)
r.Handle("/graphql", srv)handler.NewDefaultServer(graphqlSchema) membuat server GraphQL (misal gqlgen), lalu r.Handle("/graphql", srv) mendaftarkannya. Route REST dan GraphQL hidup berdampingan di router yang sama.
Gabungkan semuanya dalam satu http.Server:
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/ws", wsHandler)
r.Get("/events", sseHandler)
r.Handle("/graphql", graphQLHandler)
r.Mount("/v1", gatewayHandler)
r.Get("/health", healthHandler)
http.ListenAndServe(":8080", r)
}r.Handle("/graphql", graphQLHandler) dan r.Mount("/v1", gatewayHandler) menempelkan semua protokol di satu router. Satu port, satu middleware stack, satu lifecycle — operasional jadi jauh lebih sederhana.
Semua protokol ini berakhir sebagai http.Handler. Itulah kekuatan desain chi: tidak perlu tahu apa yang terjadi di dalam handler, cukup meneruskannya. Selama sesuatu bisa dijadikan http.Handler, chi bisa menyalurkannya.
Inti yang harus dibawa pulang:
upgrader.Upgrade lalu loop baca-tulis.CheckOrigin untuk melindungi koneksi WebSocket.http.Flusher dan format data: ....req.Context().Done() memberhentikan streaming saat klien pergi.http.Handler yang tinggal di-mount.Di episode 17 selanjutnya kita memastikan semua itu tidak rusak: testing dan benchmark — httptest untuk handler dan middleware, menguji subrouter, mocking service, serta benchmark dengan testing.B untuk membandingkan performa rute.