Skip to content

Request Validation

pkg/validation/fiber and pkg/validation/grpc add request validation to the HTTP and gRPC transports. Both fail as an *errors.AppError{Code: VALIDATION} carrying one field violation per failed field, so the Phase 5 error renderers produce byte-identical violation shapes across transports — application/problem+json with invalid_params for Fiber, codes.InvalidArgument + errdetails.BadRequest for gRPC.

Each lives in its own module (import aliased to avoid clashing with gofiber/fiber and google.golang.org/grpc):

import (
valfiber "github.com/Vilsol/lakta/pkg/validation/fiber"
valgrpc "github.com/Vilsol/lakta/pkg/validation/grpc"
)

valfiber.New builds a fiber.StructValidator backed by go-playground/validator/v10. Register it on the Fiber module via WithStructValidator; ctx.Bind().Body(&dto) then auto-invokes it, and a validate: struct-tag failure becomes a VALIDATION AppError.

fiberserver.NewModule(
fiberserver.WithStructValidator(valfiber.New()),
)

Field paths in the rendered error are normalized to their json-tag dotted/bracketed form (user.email, items[0].qty) so they match the gRPC transport exactly.

Option Effect
valfiber.WithValidate(v *validator.Validate) Supply a pre-built validator instance (custom validators, aliases). Defaults to validator.New().
valfiber.WithTagName(name string) Override the rule-tag key. Defaults to "validate".

valgrpc.NewValidator compiles a protovalidate validator that runs CEL constraints declared in your .proto files. Wire the interceptors onto the gRPC server module:

v, err := valgrpc.NewValidator()
if err != nil {
return err
}
grpcserver.NewModule(
grpcserver.WithService(&v1.MyService_ServiceDesc, NewServer()),
grpcserver.WithUnaryInterceptor(valgrpc.UnaryServerInterceptor(v)),
grpcserver.WithStreamInterceptor(valgrpc.StreamServerInterceptor(v)),
)

The unary interceptor validates each request before the handler runs; the stream interceptor validates every message received on the stream. Non-proto requests are skipped. Pass an existing protovalidate.Validator to NewValidator to share compiled constraints across servers.

Violation reasons are normalized to the final segment of the CEL rule id (string.emailemail) so they line up with validator/v10’s tag vocabulary on the Fiber side.