Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
13 Jul 2025 · 3 min read ·Article 35 / 110
Go

35 Validating Requests Using Interceptors

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

When building modern backend applications with gRPC, ensuring the validity of request data is a crucial concern. Just like with REST, in gRPC we can use an interceptor to validate requests in a centralized way before they reach the core logic in the service handler.

This article walks through a case study of how to build a validation interceptor on a gRPC server in Golang — complete with code examples, a flow diagram, and a table of request scenarios.


The Interceptor Concept in gRPC

An interceptor in gRPC Go works much like middleware in Express. Interceptors can be attached to:

  • Unary RPC: for ordinary request-response calls
  • Streaming RPC: for incoming and outgoing data streams

Interceptors can be used for:

  • Input validation
  • Logging
  • Monitoring
  • Authentication
  • Error handling

In this case study, we’ll focus on a Unary Interceptor for input validation.


Case Study: Validating the CreateUser Request

Request Schema

protobuf
 1message CreateUserRequest {
 2  string name = 1;
 3  string email = 2;
 4  int32 age = 3;
 5}
 6
 7message CreateUserResponse {
 8  string message = 1;
 9}
10
11service UserService {
12  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
13}

Validation Rules We Want:

  • name must be a non-empty string
  • email must have a valid format
  • age is optional, but if present it must be >= 0

Implementing the Validation Interceptor

1. Interceptor Function

go
 1func ValidationInterceptor(
 2  ctx context.Context,
 3  req interface{},
 4  info *grpc.UnaryServerInfo,
 5  handler grpc.UnaryHandler,
 6) (interface{}, error) {
 7
 8  switch r := req.(type) {
 9  case *pb.CreateUserRequest:
10    if r.GetName() == "" {
11      return nil, status.Error(codes.InvalidArgument, "Name is required")
12    }
13    if !isValidEmail(r.GetEmail()) {
14      return nil, status.Error(codes.InvalidArgument, "Email is invalid")
15    }
16    if r.Age < 0 {
17      return nil, status.Error(codes.InvalidArgument, "Age must be >= 0")
18    }
19  }
20
21  // continue to the handler
22  return handler(ctx, req)
23}
24
25func isValidEmail(email string) bool {
26  re := regexp.MustCompile(`^[^@\s]+@[^@\s]+\.[^@\s]+$`)
27  return re.MatchString(email)
28}

2. Registering the Interceptor with the Server

go
1s := grpc.NewServer(
2  grpc.UnaryInterceptor(ValidationInterceptor),
3)
4pb.RegisterUserServiceServer(s, &UserService{})

gRPC Request Flow with the Validation Interceptor

MERMAID
sequenceDiagram
  participant Client
  participant gRPCServer
  participant Interceptor
  participant Handler

  Client->>gRPCServer: CreateUser(request)
  gRPCServer->>Interceptor: Validasi input
  alt Valid
    Interceptor->>Handler: Lanjut handler
    Handler-->>Client: Response OK
  else Tidak valid
    Interceptor-->>Client: Error 400 (InvalidArgument)
  end

Validation Scenario Table

#Request PayloadResponseStatus
1Name OK, email OK, age 20OKOK
2Empty nameError: Name requiredInvalidArgument
3Invalid emailError: Email invalidInvalidArgument
4Age -5Error: Age invalidInvalidArgument
5Empty emailError: Email invalidInvalidArgument

Benefits of Using an Interceptor for Validation

  • Separation of concerns (SoC): the handler deals only with the core logic
  • Consistency: uniform validation across every handler
  • Easy to test: the interceptor can be unit-tested
  • Reusability: it can be extended to other endpoints

Conclusion

Validating requests with a gRPC interceptor in Golang is an efficient, clean, and idiomatic approach. With this technique, we can keep handlers clean and centralize all validation rules inside the interceptor, making the codebase more scalable and maintainable.

Use this approach in every RPC service that needs validation — and combine it with authentication, rate limiting, and logging to build a solid, professional backend system.

Related Articles

💬 Comments