distributedsystem/microservice/grpc

Interceptors | gRPC
‘Chapter 5. gRPC: Beyond the Basics’
—Kasun Indrasiri, “gRPC”

Interceptors in gRPC are middleware function that intercept and process requests/responses before they reach the actual gRPC service methods(server side) or before they are sent to the server(client-side).
(Similar to HTTP middleware in frameworks like Express.)

Interceptors are well suited to implementing logic that is not specific to a single RPC method.

  1. Unary Interceptors —> For Single request-response calls.
  2. Stream Interceptors —> For Streaming calls(client, server or bidirectional)

Server Side Interceptors

Client Side Interceptors

Unary Interceptors

A Unary interceptors is used for normal(non-streaming) gRPC calls.

import (
	"context"
	"log"
	"time"
 
	"google.golang.org/grpc"
)
 
// Unary Interceptor function
func LoggingUnaryInterceptor(
	ctx context.Context,
	req interface{},
	info *grpc.UnaryServerInfo,
	handler grpc.UnaryHandler,
) (interface{}, error) {
	start := time.Now()
	log.Printf("Unary Interceptor: Received call to %s", info.FullMethod)
 
	// Call the actual gRPC handler
	resp, err := handler(ctx, req)
 
	log.Printf("Completed: %s | Duration: %s | Error: %v", info.FullMethod, time.Since(start), err)
	return resp, err
}
grpcServer := grpc.NewServer(
	grpc.UnaryInterceptor(LoggingUnaryInterceptor),
)

Stream Interceptors

import (
	"log"
 
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)
 
// Stream Interceptor function
func AuthStreamInterceptor(
	srv interface{},
	ss grpc.ServerStream,
	info *grpc.StreamServerInfo,
	handler grpc.StreamHandler,
) error {
	md, ok := metadata.FromIncomingContext(ss.Context())
	if !ok {
		return grpc.Errorf(grpc.Code(grpc.Unauthenticated), "Missing metadata")
	}
 
	// Extract token from metadata
	token := md["authorization"]
	if len(token) == 0 || token[0] != "valid-token" {
		return grpc.Errorf(grpc.Code(grpc.Unauthenticated), "Invalid token")
	}
 
	log.Printf("Authenticated Stream Call: %s", info.FullMethod)
 
	// Call the actual stream handler
	return handler(srv, ss)
}
grpcServer := grpc.NewServer(
	grpc.StreamInterceptor(AuthStreamInterceptor),
)