Ketika membangun aplikasi distributed system dengan gRPC, sering kali kita membutuhkan logging, monitoring, validasi, atau transformasi data pada request/response yang lewat antara client dan server. Namun, tentunya tidak bijak jika setiap logika tambahan itu kita selipkan pada tiap pemanggilan RPC. Untungnya, gRPC menyediakan konsep interceptor yang sangat powerful dan modular. Salah satu yang paling sering digunakan adalah Unary Interceptor di sisi client.
Artikel ini akan membahas secara mendalam apa, kenapa, dan bagaimana menambahkan Unary Interceptor di gRPC client dengan contoh pada bahasa Go. Kita akan membedah kasus penggunaan umum, menampilkan kode, serta melakukan simulasi dan visualisasi data flow dengan diagram.
Apa itu Unary Interceptor di Client?
Secara sederhana, interceptor adalah middleware (atau hook) yang bisa kita selipkan ke dalam life-cycle pemanggilan RPC. Pada unary RPC (RPC yang satu request satu response), unary interceptor berfungsi mengintersepsi tiap request outgoing ke server sebelum benar-benar terjadi serta memperlakukan response sebelum akhirnya diberikan ke aplikasi.
Menggunakan interceptor memungkinkan kita untuk:
- Logging request dan response
- Mengatur retry, timeout, deadline
- Validasi data sebelum dikirim
- Melakukan tracing (OpenTelemetry, Jaeger, dll.)
- Menangani authentication/sekuritas (menambah header, dsb)
- Custom error handling
Karena sifatnya yang reusable, kode menjadi lebih rapih, maintainable, dan DRY (Don’t Repeat Yourself).
Skema Alur Unary Interceptor
Mari lihat dulu bagaimana data mengalir saat kita gunakan unary interceptor pada client.
graph LR A[Application Code] --> B[Unary Interceptor] B --> C[gRPC Client Stub] C --> D[gRPC Server] D --> C C --> B B --> A
- A: Code aplikasi yang memanggil RPC.
- B: Interceptor, intercept request (dan response).
- C: Library/stub gRPC.
- D: Server gRPC.
Interceptor akan dipanggil setiap kali ada call ke method gRPC apa pun. Di dalam interceptor, kita bisa melakukan logic tambahan sebelum/atau sesudah invoker()
(handler sebenarnya).
Cara Menambahkan Unary Interceptor di Client (Go)
1. Persiapan
Pastikan Anda punya project gRPC dan dependency berikut:
go get google.golang.org/grpc
Kita asumsikan sudah terdapat stub hasil generate proto (misal: pb.MyServiceClient
).
2. Implementasi Unary Interceptor
Sintaks general unary interceptor di Go seperti berikut:
func MyUnaryClientInterceptor(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
// Logic sebelum request
err := invoker(ctx, method, req, reply, cc, opts...)
// Logic setelah request
return err
}
invoker()
adalah call sebenarnya ke service. Anda wajib memanggilnya, kecuali memang ingin menolak kirm request ke server.- Bisa tempatkan logic sebelum (pre), sesudah (post), atau malah mem-block/passthrough.
Contoh: Logging Interceptor
Misal, kita ingin mencatat waktu request dan respon tiap kali client memanggil RPC method.
import (
"context"
"log"
"time"
"google.golang.org/grpc"
)
func LoggingUnaryClientInterceptor(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
start := time.Now()
log.Printf("[Client Interceptor] >> Outgoing request: %s", method)
err := invoker(ctx, method, req, reply, cc, opts...) // kirim request
elapsed := time.Since(start)
log.Printf("[Client Interceptor] << Response from %s in %s Err:%v", method, elapsed, err)
return err
}
3. Attach Interceptor ke Client
Pada gRPC Go, unary interceptor di-attach pada dial option saat membuat koneksi client.
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(LoggingUnaryClientInterceptor),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewMyServiceClient(conn)
Setelah ini, setiap RPC unary yang dipanggil oleh client secara otomatis akan melewati middleware LoggingUnaryClientInterceptor yang kita definisikan.
Studi Kasus: Menambah Authentication Token
Anggap kita ingin menyisipkan header auth token di setiap request, supaya server bisa mengenali identitas klien.
import (
"context"
"google.golang.org/grpc/metadata"
)
func AuthTokenUnaryClientInterceptor(token string) grpc.UnaryClientInterceptor {
return func(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
// Sisipkan metadata header auth
md := metadata.Pairs("authorization", "Bearer "+token)
ctx = metadata.NewOutgoingContext(ctx, md)
return invoker(ctx, method, req, reply, cc, opts...)
}
}
Attach pada waktu dialing:
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(AuthTokenUnaryClientInterceptor("token123")),
)
Chaining Multiple Interceptor
Pada Go 1.36+, gunakan third-party untuk chaining (mis: grpc_middleware
). Contoh:
import (
"github.com/grpc-ecosystem/go-grpc-middleware"
)
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithUnaryInterceptor(
grpc_middleware.ChainUnaryClient(
LoggingUnaryClientInterceptor,
AuthTokenUnaryClientInterceptor("token123"),
),
),
)
Setiap interceptor dijalankan sesuai urutannya dalam chain.
Simulasi Output
Bayangkan client memanggil satu method GetUser(ctx, req)
ke server. Dengan dua interceptor: Logging dan Auth.
Tabel Simulasi Proses:
Step | Module | Action | Detail |
---|---|---|---|
1 | App Code | Call GetUser() | |
2 | Logging Int. | Catat waktu mulai, log outgoing method | GetUser |
3 | Auth Int. | Sisipkan Auth header ke outgoing context | Header: Bearer token123 |
4 | gRPC stub | Kirimkan request ke server | |
5 | Logging Int. | Hitung waktu, log response | Tampilkan latency |
Best Practice
- Selalu panggil
invoker()
di dalam interceptor agar request tetap berjalan. - Pisahkan logic ke beberapa interceptor sesuai concern (logging, auth, tracing).
- Pastikan urutan interceptor sesuai kebutuhan (misal: inject header sebelum logging/after).
- Gunakan chain/stack bila perlu sehingga mudah dipelihara dan scalable.
- Testing interceptor secara unit & integration untuk memastikan tidak mengubah behavior aplikasi secara tidak sengaja.
Kesimpulan
Menambahkan unary interceptor di client gRPC adalah teknik yang sangat powerful untuk menjaga kode clean, DRY, dan scalable meskipun aplikasi semakin kompleks. Baik itu untuk logging, monitoring, auth, atau apapun, interceptor menawarkan satu tempat sentral untuk mengembangkan behavior aplikasi secara modular.
Jika Anda baru mulai menggunakan gRPC, cobalah implementasi di atas dan lihat betapa mudahnya memperkaya behavior client tanpa harus mengubah satu per satu call Anda.
Jangan ragu untuk bereksperimen dengan use-case lain menggunakan dasar interceptor ini, dan happy coding!
Referensi
19 Menambahkan Interceptor Unary di Server
Artikel Terhangat
21 Memahami Konsep Streaming pada gRPC
06 Jun 2025
20 Menambahkan Interceptor Unary di Client
06 Jun 2025
19 Menambahkan Interceptor Unary di Server
06 Jun 2025
18 Membaca Metadata di Server gRPC
06 Jun 2025
17 Menambahkan Metadata pada gRPC Request
06 Jun 2025
16 Penanganan Error di Server gRPC
06 Jun 2025

21 Memahami Konsep Streaming pada gRPC

20 Menambahkan Interceptor Unary di Client

19 Menambahkan Interceptor Unary di Server

18 Membaca Metadata di Server gRPC

17 Menambahkan Metadata pada gRPC Request
