Implementing Interceptors

1. Creating Unary Server Interceptor

Example: Logging unary server interceptor

func LoggingUnary(ctx context.Context, req any, info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler) (any, error) {
    start := time.Now()
    res, err := handler(ctx, req)
    log.Printf("%s %v code=%s", info.FullMethod, time.Since(start), status.Code(err))
    return res, err
}
ParamPurpose
info.FullMethod"/pkg.Service/Method"
handlerCall to invoke next interceptor / handler

2. Creating Stream Server Interceptor

Example: Stream interceptor

func StreamLog(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo,
    handler grpc.StreamHandler) error {
    start := time.Now()
    err := handler(srv, ss)
    log.Printf("stream %s %v", info.FullMethod, time.Since(start))
    return err
}

3. Creating Unary Client Interceptor

Example: Auth-injecting client interceptor

func AuthUnary(token string) grpc.UnaryClientInterceptor {
    return func(ctx context.Context, method string, req, reply any,
        cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
        ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token)
        return invoker(ctx, method, req, reply, cc, opts...)
    }
}

4. Creating Stream Client Interceptor

SignatureDetail
StreamClientInterceptorWraps stream creation; can wrap returned ClientStream
UsePer-message logging, metrics on Send/Recv

5. Chaining Interceptors

Example: Chain on server & client

srv := grpc.NewServer(
    grpc.ChainUnaryInterceptor(Recovery, Logging, Auth, Validation),
    grpc.ChainStreamInterceptor(StreamRecovery, StreamLog),
)
conn, _ := grpc.NewClient(addr,
    grpc.WithChainUnaryInterceptor(Retry, Tracing, AuthUnary(token)),
)
OrderDetail
First listedOutermost — runs first on inbound, last on return
Recovery firstCatches panics from all inner interceptors

6. Implementing Logging Interceptor

FieldSource
methodinfo.FullMethod
durationtime.Since(start)
status codestatus.Code(err)
request idFrom incoming metadata

7. Implementing Auth Interceptor

Example: Token validation interceptor

func AuthInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler) (any, error) {
    if isPublic(info.FullMethod) { return handler(ctx, req) }
    md, _ := metadata.FromIncomingContext(ctx)
    tok := strings.TrimPrefix(first(md, "authorization"), "Bearer ")
    claims, err := jwt.Parse(tok, key)
    if err != nil { return nil, status.Error(codes.Unauthenticated, "invalid token") }
    return handler(context.WithValue(ctx, ctxKey{}, claims), req)
}

8. Implementing Recovery Interceptor

Example: Panic recovery

func Recovery(ctx context.Context, req any, info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler) (res any, err error) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("panic %s: %v\n%s", info.FullMethod, r, debug.Stack())
            err = status.Error(codes.Internal, "internal error")
        }
    }()
    return handler(ctx, req)
}
TipDetail
Place outermostCatches panics from other interceptors + handler
Librarygithub.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery

9. Modifying Request and Response

ConcernDetail
Avoid mutationGenerated messages are not designed for in-place edits
Wrap responseReplace fields via cloned message
Stream wrappersEmbed grpc.ServerStream and override SendMsg/RecvMsg

10. Registering Interceptors

SideAPI
Server unarygrpc.UnaryInterceptor / ChainUnaryInterceptor
Server streamgrpc.StreamInterceptor / ChainStreamInterceptor
Client unarygrpc.WithUnaryInterceptor / WithChainUnaryInterceptor
Client streamgrpc.WithStreamInterceptor / WithChainStreamInterceptor