tutorial

19 Menambahkan Interceptor Unary di Server

Saat membangun aplikasi backend berbasis gRPC, salah satu konsep powerful yang wajib dikuasai adalah interceptor. Khususnya pada server-side, unary interceptor berperan sangat krusial: mulai dari logging, authentication, hingga metrics. Pada artikel ini, kita akan mengupas tuntas cara menambahkan unary interceptor di server gRPC, plus skenario nyata, contoh kode, bahkan flow diagram agar mudah kamu praktikkan.


Apa Itu gRPC Interceptor?

Secara analogi, interceptor pada gRPC mirip “middleware” pada ExpressJS atau HTTP pipeline di ASP.NET Core. Interceptor pada gRPC memungkinkan kita untuk “mempersingkat” atau “memperpanjang” proses sebelum atau sesudah eksekusi RPC secara generik.

Unary Interceptor menangani permintaan satu-kali (single request, single response), berbeda dengan stream interceptor.


Mengapa Membutuhkan Unary Interceptor?

Unary interceptor sangat cocok untuk beberapa hal berikut:

  • Logging: Mencatat semua request, response, dan error.
  • Authentication & Authorization: Memvalidasi token/jwt sebelum eksekusi handler.
  • Monitoring: Mengirim metrics ke Prometheus, DataDog, dsb.
  • Transformasi Request/Response: Modifikasi data sebelum/ setelah handler.

Dengan demikian, kode bisnis tidak tercampur concerns lain—clean code, DRY, dan testable.


Diagram Alur Interceptor Unary

Untuk memperjelas alur data saat unary interceptor berjalan, simak diagram berikut:

flowchart LR
    A(Client) --> B(gRPC Server)
    B --> C(Interceptor)
    C --> D(Service Handler)
    D --> C
    C --> B
    B --> A

Pada flow di atas, Interceptor berada di antara client request dan handler utama, memungkinkan kita menyisipkan logika tambahan sebelum dan sesudah core handler berjalan.


Contoh Kasus: Logging Interceptor pada Server

Misalkan aplikasi kita memiliki service sederhana: HelloService dengan method SayHello.

1. Definisi Proto (hello.proto)

syntax = "proto3";

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Proses berikutnya: generate kode stub untuk server.


2. Implementasi gRPC Server + Unary Interceptor

Kita asumsikan menggunakan Golang dan package google.golang.org/grpc, karena dokumentasi dan komunitasnya sangat luas.

a. Membuat Logging Interceptor

import (
    "context"
    "log"

    "google.golang.org/grpc"
)

func LoggingUnaryInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (resp interface{}, err error) {
    log.Printf("Received request for method: %s", info.FullMethod)
    resp, err = handler(ctx, req) // Menjalankan handler sesungguhnya
    if err != nil {
        log.Printf("Error executing %s: %v", info.FullMethod, err)
    } else {
        log.Printf("Response: %+v", resp)
    }
    return resp, err
}

b. Mendaftarkan Interceptor pada Server

import (
    "google.golang.org/grpc"
    pb "path/to/generated/hello" // sesuaikan path
)

func main() {
    server := grpc.NewServer(
        grpc.UnaryInterceptor(LoggingUnaryInterceptor), // Attach interceptor di sini
    )
    pb.RegisterHelloServiceServer(server, &HelloService{})
    // ... binding listener dll.
}

c. Implementasi Handler Utama

type HelloService struct {
    pb.UnimplementedHelloServiceServer
}

func (s *HelloService) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello, " + req.Name + "!"}, nil
}

Simulasi: Output Logging

Tabel berikut ini menunjukkan simulasi output interceptor jika ada client mengirim request:

MethodRequestResponseError
/HelloService/SayHello{ “name”: “Kiki” }{ “message”: “Hello, Kiki!” }-

Output log (stdout):

Received request for method: /HelloService/SayHello
Response: {Message:"Hello, Kiki!"}

Kombinasi Multiple Interceptor

Bagaimana jika kita ingin stack beberapa interceptor (misal: authentication + logging)? Di Go, gunakan grpc.ChainUnaryInterceptor:

server := grpc.NewServer(
    grpc.ChainUnaryInterceptor(
        AuthenticationInterceptor,
        LoggingUnaryInterceptor,
        // dst.
    ),
)

Chain akan dijalankan urut; handler terakhir tetap ada di ujung.


Studi Kasus Lain: Authentication Interceptor

Sebagai pelengkap, inilah contoh interceptor sederhana untuk memvalidasi header authorization.

func AuthUnaryInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok || len(md["authorization"]) == 0 {
        return nil, status.Error(codes.Unauthenticated, "missing auth token")
    }
    token := md["authorization"][0]
    if token != "mysecrettoken" {
        return nil, status.Error(codes.Unauthenticated, "invalid auth token")
    }
    return handler(ctx, req)
}

Jika client tidak menyertakan header authorization, permintaan akan otomatis rejected tanpa handler utama dieksekusi.


Best Practice: Kapan Interceptor Digunakan?

Kebutuhan MiddlewareCocok Interceptor?
Access Logging
Rate Limiting
Caching Satu Endpoint✗ (lebih baik inline handler)
Authentication Global
Service-level Validation
Data Enrichment Sederhana✗ (prefer handler)

Kesimpulan

Dengan menambahkan interceptor unary di server gRPC, Anda bisa menerapkan middleware elegan, powerful, dan clean code ala enterprise. Cara setup-nya straightforward, namun impact-nya masif untuk maintainability dan scalability backend microservices Anda.

Kalau menemukan use case menarik, silakan eksperimen menambah interceptor sendiri—dan jangan takut kombinasi beberapa sekaligus. Inilah one of best practices service backend modern yang wajib dikuasai engineer masa kini!


Referensi Lanjutan:


Semoga artikel ini bermanfaat! Jangan ragu bertanya di kolom komentar atau tinggalkan pengalamanmu memakai interceptor di production.

comments powered by Disqus

Topik Terhangat

programming
174
tips-and-trick
43
tutorial
40
jaringan
28
hardware
11
linux
4
kubernetes
1