Ketika membangun servis gRPC di ekosistem Go, sangat sering kita dihadapkan pada kebutuhan untuk memasang fungsi middleware: entah itu logging request, validasi autentikasi, audit, monitoring, hingga pengelolaan error dengan pola yang konsisten. Di dunia REST, hal seperti ini jamak dilakukan lewat middleware seperti pada Gin atau Echo. Sementara di gRPC, konsep yang setara disebut interceptor.
Pada artikel ini, saya akan membahas secara praktis tentang Unary Interceptor pada gRPC: mulai dari pemahaman konsep, arsitektur, implementasi, hingga tips produksi. Kita juga akan langsung membuat sebuah custom unary interceptor dari awal, lengkap dengan contoh kode, simulasi, dan diagram alur.
Apa Itu Unary Interceptor?
Interceptor pada gRPC adalah fungsi yang “menyela” proses permintaan (request) dari client ke server, sebelum method handler dieksekusi. Tipe utama interceptor di gRPC ada dua:
- Unary interceptor – Untuk method unary (satu request, satu response).
- Stream interceptor – Untuk streaming method (client, server, atau bidi).
Pada artikel ini kita akan fokus pada unary interceptor.
Mengapa Perlu Menggunakan Unary Interceptor?
Bayangkan Anda ingin memastikan semua endpoint API Anda diaudit, atau ingin tiap request di-log, atau validasi JWT dilakukan sebelum masuk ke logic utama. Jika menulis kode itu berulang-ulang di tiap handler, kode menjadi tidak reusable dan rawan bug. Di sinilah unary interceptor jadi solusi.
Sederhananya, dengan unary interceptor, kita punya satu tempat di mana logika tambahan dapat “memotong” semua request, baik sebelum atau sesudah handler utama dijalankan.
Diagram Alur Unary Interceptor
Mari kita visualisasikan prosesnya dengan flowchart menggunakan Mermaid:
flowchart TD A(Client gRPC Request) --> B[Unary Interceptor] B --> C{Pre-Processing} C -->|Pass| D["Handler (Your Actual gRPC Service)"] D --> E{Post-Processing} E --> F[gRPC Response] C -->|Blocked/Error| G[Return Error to Client]
Anatomy dari Unary Interceptor di Go
Mari kita lihat bentuk signature dari unary interceptor pada Go:
func(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error)
Penjelasan parameter:
ctx
: Context dari request, bisa dipakai untuk propagasi nilai custom (misal: user claim).req
: Payload dari request.info
: Berisi meta-informasi (fully qualified method name, dsb).handler
: Fungsi yang mengeksekusi handler sebenarnya.
Biasanya, kita menjalankan pre-processing, lalu memanggil handler(ctx, req)
, dan menambah post-processing jika perlu.
Contoh Implementasi Unary Interceptor: Logging
Untuk memulai, mari buat interceptor sederhana yang me-log tiap request masuk, lengkap dengan waktu eksekusinya.
import (
"context"
"log"
"time"
"google.golang.org/grpc"
)
func LoggingUnaryInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
start := time.Now()
log.Printf("gRPC method: %s - req: %+v", info.FullMethod, req)
resp, err := handler(ctx, req)
log.Printf(
"gRPC method: %s - resp: %+v - err: %v - duration: %s",
info.FullMethod, resp, err, time.Since(start),
)
return resp, err
}
Cara Memasang Interceptor di Server
Setelah interceptor selesai, kita pasang saat membuat gRPC server:
server := grpc.NewServer(
grpc.UnaryInterceptor(LoggingUnaryInterceptor),
)
Bisa juga chain beberapa unary interceptor sekaligus menggunakan grpc_middleware:
import "github.com/grpc-ecosystem/go-grpc-middleware"
server := grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
AuthUnaryInterceptor,
LoggingUnaryInterceptor,
MetricsUnaryInterceptor,
),
)
Simulasi: Membandingkan Sebelum & Sesudah Interceptor
Berikut simulasi proses request dengan dan tanpa unary interceptor:
Tanpa Interceptor | Dengan Interceptor | |
---|---|---|
1 | gRPC Request Masuk | gRPC Request Masuk |
2 | Handler dijalankan | Pre-processing oleh Interceptor |
3 | Handler dijalankan | Handler dijalankan |
4 | Response dikirim ke klien | Post-processing oleh Interceptor |
5 | Response dikirim ke klien |
Studi Kasus: Auth Interceptor dengan JWT
Mari kita buat sebuah interceptor yang melakukan validasi token JWT dari metadata sebelum handler dieksekusi. Bila token tidak valid, langsung return error; jika sah, propagate user claim ke context.
Implementasi:
import (
"context"
"errors"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func AuthUnaryInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, errors.New("missing metadata")
}
// Ambil Authorization header
tokens := md["authorization"]
if len(tokens) == 0 {
return nil, errors.New("missing authorization token")
}
token := strings.TrimPrefix(tokens[0], "Bearer ")
// Validasi token di sini (pseudocode)
claims, err := ValidateJWT(token)
if err != nil {
return nil, errors.New("invalid token")
}
// Propagate claim ke context
newCtx := context.WithValue(ctx, "user", claims)
// Teruskan ke handler utama dengan context baru
return handler(newCtx, req)
}
Handler Dapat Mengakses Claim User
user := ctx.Value("user").(UserClaims)
// sekarang user sudah bisa diakses pada handler
Best Practices dalam Menulis Unary Interceptor
- Sederhanakan logika: Pastikan tidak terlalu berat di dalam interceptor agar latency tidak bengkak.
- Tangani error secara eksplisit: Jika terjadi error, pastikan return error yang informatif.
- Propagasi context dengan baik: Insert apapun ke context dengan jelas agar handler bisa mengakses.
- Pisahkan urusan pre dan post processing: Pisahkan logika sebelum dan sesudah handler dijalankan.
- Chain interceptor sesuai urutan: Misal: Auth → Logging → Metrics.
Memahami Chain dan Komposisi Interceptor
Salah satu kelebihan interceptor adalah mudah dikomposisi. Dengan chain, urutan eksekusi bisa digambarkan sebagai berikut:
sequenceDiagram participant Client participant Interceptor1 participant Interceptor2 participant Handler Client->>Interceptor1: Request Interceptor1->>Interceptor2: Pre-processing Interceptor2->>Handler: Pre-processing Handler-->>Interceptor2: Response Interceptor2-->>Interceptor1: Post-processing Interceptor1-->>Client: Return Response
Kesimpulan
Unary interceptor adalah fitur esensial untuk membangun gRPC service yang robust, concise, dan maintainable. Dengan memahami arsitektur dan pola implementasinya, kita bisa dengan mudah menambah fungsionalitas seperti logging, autentikasi, rate-limiting, hingga observability tanpa code duplication pada tiap handler.
Dengan contoh-contoh dan studi kasus tadi, Anda sekarang siap menulis unary interceptor sendiri sesuai kebutuhan aplikasi Anda! Jangan lupa, desain interceptor dengan efisiensi dan clarity yang baik untuk menghasilkan servis-produk yang handal dan scalable.
Referensi:
Salam refactor!
31 Apa Itu Interceptor dalam gRPC?
Artikel Terhangat
32 Menulis Unary Interceptor Sendiri
07 Jul 2025
31 Apa Itu Interceptor dalam gRPC?
07 Jul 2025
30 Studi Kasus: Streaming Progress Update
07 Jul 2025
29 Studi Kasus: Streaming Upload File
07 Jul 2025

32 Menulis Unary Interceptor Sendiri

31 Apa Itu Interceptor dalam gRPC?

30 Studi Kasus: Streaming Progress Update
