Documentation
¶
Overview ¶
Package grpc provides a production-ready gRPC server with auto-discovery, interceptors, and lifecycle integration for the gaz framework.
Overview ¶
This package implements a gRPC server that integrates with gaz's DI container and lifecycle management. Services implementing the Registrar interface are automatically discovered and registered on startup.
Quick Start ¶
Use the module to register the gRPC server with your application:
app := gaz.New()
app.Use(grpc.NewModule(
grpc.WithPort(50051),
grpc.WithReflection(true),
))
Service Registration ¶
Services implement the Registrar interface to be auto-discovered:
type GreeterService struct {
pb.UnimplementedGreeterServer
}
func (s *GreeterService) RegisterService(server grpc.ServiceRegistrar) {
pb.RegisterGreeterServer(server, s)
}
Register the service in your module:
di.For[*GreeterService](c).Provider(NewGreeterService)
The gRPC server will discover and register all Registrar implementations automatically on startup.
Interceptors ¶
The server includes built-in interceptors for:
- Logging: Request/response logging with duration and status
- Recovery: Panic recovery with stack trace logging
Reflection ¶
gRPC reflection is enabled by default, allowing tools like grpcurl to introspect available services:
grpcurl -plaintext localhost:50051 list grpcurl -plaintext localhost:50051 describe mypackage.MyService
Disable reflection in production if needed:
grpc.NewModule(grpc.WithReflection(false))
Configuration ¶
Configuration can be provided via config file or module options:
servers:
grpc:
port: 50051
reflection: true
max_recv_msg_size: 4194304
max_send_msg_size: 4194304
Index ¶
- Constants
- func InterceptorLogger(l *slog.Logger) logging.Logger
- func NewLoggingInterceptor(logger *slog.Logger) (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
- func NewModule() gaz.Module
- func NewRecoveryInterceptor(logger *slog.Logger, devMode bool) (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
- type AlwaysPassLimiter
- type AuthBundle
- type AuthFunc
- type Config
- type InterceptorBundle
- type Limiter
- type LoggingBundle
- type RateLimitBundle
- type RecoveryBundle
- type Registrar
- type Server
- type ValidationBundle
Constants ¶
const ( // PriorityLogging is the priority for the logging interceptor (runs first). PriorityLogging = 0 // PriorityRateLimit is the priority for the rate limit interceptor (after logging, before auth). PriorityRateLimit = 25 // PriorityAuth is the priority for the auth interceptor (after logging, before validation). PriorityAuth = 50 // PriorityValidation is the priority for the validation interceptor. PriorityValidation = 100 // PriorityRecovery is the priority for the recovery interceptor (runs last). PriorityRecovery = 1000 )
Priority constants for built-in interceptors. Custom interceptors should use values between PriorityLogging and PriorityRecovery.
const DefaultHealthCheckInterval = 5 * time.Second
DefaultHealthCheckInterval is the default interval for health checks.
const DefaultMaxMsgSize = 4 * 1024 * 1024
DefaultMaxMsgSize is the default maximum message size (4MB).
const DefaultPort = 50051
DefaultPort is the default port for the gRPC server.
Variables ¶
This section is empty.
Functions ¶
func InterceptorLogger ¶
InterceptorLogger adapts slog.Logger to the go-grpc-middleware logging.Logger interface. This allows the gRPC logging interceptor to use gaz's standard slog-based logger.
func NewLoggingInterceptor ¶
func NewLoggingInterceptor(logger *slog.Logger) (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
NewLoggingInterceptor creates logging interceptors for gRPC requests. The interceptors log request start, completion, duration, and status.
Returns both unary and stream server interceptors.
func NewModule ¶
NewModule creates a gRPC module. Returns a gaz.Module that registers gRPC server components.
Components registered:
- grpc.Config (loaded from flags/config)
- *grpc.LoggingBundle (logging interceptor)
- *grpc.RateLimitBundle (rate limit interceptor, uses AlwaysPassLimiter unless Limiter registered)
- *grpc.AuthBundle (auth interceptor, only if AuthFunc registered)
- *grpc.ValidationBundle (protovalidate interceptor)
- *grpc.RecoveryBundle (panic recovery interceptor)
- *grpc.Server (eager, starts on app start)
Custom interceptors can be added by registering implementations of InterceptorBundle in the DI container. They will be auto-discovered and chained based on their Priority().
Example:
app := gaz.New() app.Use(grpc.NewModule())
Adding custom interceptors:
// Register your interceptor bundle
gaz.For[*MyInterceptor](c).Provider(NewMyInterceptor)
// MyInterceptor implements grpc.InterceptorBundle
type MyInterceptor struct{}
func (m *MyInterceptor) Name() string { return "my-interceptor" }
func (m *MyInterceptor) Priority() int { return 500 } // Between validation (100) and recovery (1000)
func (m *MyInterceptor) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor) {
return myUnaryInterceptor, myStreamInterceptor
}
func NewRecoveryInterceptor ¶
func NewRecoveryInterceptor(logger *slog.Logger, devMode bool) (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
NewRecoveryInterceptor creates panic recovery interceptors for gRPC handlers. When a panic occurs:
- Full stack trace is logged to the provided logger
- In dev mode, panic details are returned in the error message
- In production mode, a generic "internal server error" is returned
Returns both unary and stream server interceptors.
Types ¶
type AlwaysPassLimiter ¶
type AlwaysPassLimiter struct{}
AlwaysPassLimiter is a no-op limiter that allows all requests. This is the default limiter when no custom Limiter is registered in DI.
type AuthBundle ¶
type AuthBundle struct {
// contains filtered or unexported fields
}
AuthBundle is the built-in authentication interceptor bundle. It validates requests using the registered AuthFunc.
func NewAuthBundle ¶
func NewAuthBundle(authFunc AuthFunc) *AuthBundle
NewAuthBundle creates a new auth interceptor bundle.
func (*AuthBundle) Interceptors ¶
func (b *AuthBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
Interceptors returns the auth interceptors.
func (*AuthBundle) Priority ¶
func (b *AuthBundle) Priority() int
Priority returns the auth priority (after logging, before validation).
type AuthFunc ¶
AuthFunc is the authentication function type. It extracts and validates credentials from the context, returning an enriched context or an error if authentication fails.
Use auth.AuthFromMD to extract tokens from metadata:
func myAuthFunc(ctx context.Context) (context.Context, error) {
token, err := auth.AuthFromMD(ctx, "bearer")
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid auth token: %v", err)
}
// Validate token and enrich context...
return ctx, nil
}
Register in DI to enable auth interceptor:
gaz.For[grpc.AuthFunc](c).Instance(myAuthFunc)
type Config ¶
type Config struct {
// Port is the TCP port the gRPC server listens on.
// Defaults to 50051 if not set.
Port int `json:"port" yaml:"port" mapstructure:"port" gaz:"port"`
// Reflection enables gRPC reflection for service discovery.
// When enabled, tools like grpcurl can introspect available services.
// Defaults to true.
Reflection bool `json:"reflection" yaml:"reflection" mapstructure:"reflection" gaz:"reflection"`
// MaxRecvMsgSize is the maximum message size the server can receive.
// Defaults to 4MB.
MaxRecvMsgSize int `json:"max_recv_msg_size" yaml:"max_recv_msg_size" mapstructure:"max_recv_msg_size" gaz:"max_recv_msg_size"`
// MaxSendMsgSize is the maximum message size the server can send.
// Defaults to 4MB.
MaxSendMsgSize int `json:"max_send_msg_size" yaml:"max_send_msg_size" mapstructure:"max_send_msg_size" gaz:"max_send_msg_size"`
// HealthEnabled enables the built-in gRPC health check service.
// Defaults to true.
HealthEnabled bool `json:"health_enabled" yaml:"health_enabled" mapstructure:"health_enabled" gaz:"health_enabled"`
// HealthCheckInterval is the interval for syncing health status.
// Defaults to 5 seconds.
HealthCheckInterval time.Duration `json:"health_check_interval" yaml:"health_check_interval" mapstructure:"health_check_interval" gaz:"health_check_interval"`
// DevMode enables development mode for verbose error messages.
// Defaults to false.
DevMode bool `json:"dev_mode" yaml:"dev_mode" mapstructure:"dev_mode" gaz:"dev_mode"`
// SkipListener skips binding a listener and serving.
// When true, the server still discovers registrars, registers services,
// enables reflection, and wires health — but does not bind a port or
// call server.Serve(). This is used when Vanguard handles connections.
// Defaults to false.
SkipListener bool `json:"skip_listener" yaml:"skip_listener" mapstructure:"skip_listener" gaz:"skip_listener"`
}
Config holds configuration for the gRPC server.
func (*Config) SetDefaults ¶
func (c *Config) SetDefaults()
SetDefaults applies default values to zero-value fields. Boolean fields (Reflection, HealthEnabled, SkipListener) are not set here because their zero value (false) is indistinguishable from an explicit false. Use DefaultConfig() to get safe defaults before config loading. Implements the config.Defaulter interface.
type InterceptorBundle ¶
type InterceptorBundle interface {
// Name returns a unique identifier for logging and debugging.
Name() string
// Priority determines the order in the interceptor chain.
// Lower values run earlier. Built-in interceptors use:
// - PriorityLogging (0): logging interceptor
// - PriorityRecovery (1000): recovery interceptor
// Custom interceptors should use values between 1 and 999.
Priority() int
// Interceptors returns unary and stream server interceptors.
// Either may be nil if the bundle only provides one type.
Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
}
InterceptorBundle provides gRPC interceptors for auto-discovery. Implementations are automatically discovered and chained by the gRPC server.
To add custom interceptors:
- Implement this interface
- Register the implementation in the DI container
- The server will auto-discover and chain it based on Priority()
Example:
type MetricsBundle struct{}
func (m *MetricsBundle) Name() string { return "metrics" }
func (m *MetricsBundle) Priority() int { return 50 }
func (m *MetricsBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor) {
return metricsUnary, metricsStream
}
// Register in DI:
gaz.For[*MetricsBundle](c).Provider(NewMetricsBundle)
type Limiter ¶
Limiter defines the interface for rate limiting. Implementations should return nil to allow the request, or an error to reject it.
Register a custom limiter in DI to override the default AlwaysPassLimiter:
gaz.For[grpc.Limiter](c).Instance(myLimiter)
type LoggingBundle ¶
type LoggingBundle struct {
// contains filtered or unexported fields
}
LoggingBundle is the built-in logging interceptor bundle. It logs request start, completion, duration, and status.
func NewLoggingBundle ¶
func NewLoggingBundle(logger *slog.Logger) *LoggingBundle
NewLoggingBundle creates a new logging interceptor bundle.
func (*LoggingBundle) Interceptors ¶
func (b *LoggingBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
Interceptors returns the logging interceptors.
func (*LoggingBundle) Name ¶
func (b *LoggingBundle) Name() string
Name returns the bundle identifier.
func (*LoggingBundle) Priority ¶
func (b *LoggingBundle) Priority() int
Priority returns the logging priority (runs first).
type RateLimitBundle ¶
type RateLimitBundle struct {
// contains filtered or unexported fields
}
RateLimitBundle is the built-in rate limiting interceptor bundle. It uses the registered Limiter to control request rates.
func NewRateLimitBundle ¶
func NewRateLimitBundle(limiter Limiter) *RateLimitBundle
NewRateLimitBundle creates a new rate limit interceptor bundle.
func (*RateLimitBundle) Interceptors ¶
func (b *RateLimitBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
Interceptors returns the rate limit interceptors.
func (*RateLimitBundle) Name ¶
func (b *RateLimitBundle) Name() string
Name returns the bundle identifier.
func (*RateLimitBundle) Priority ¶
func (b *RateLimitBundle) Priority() int
Priority returns the rate limit priority (after logging, before auth).
type RecoveryBundle ¶
type RecoveryBundle struct {
// contains filtered or unexported fields
}
RecoveryBundle is the built-in panic recovery interceptor bundle. It catches panics and returns appropriate error responses.
func NewRecoveryBundle ¶
func NewRecoveryBundle(logger *slog.Logger, devMode bool) *RecoveryBundle
NewRecoveryBundle creates a new recovery interceptor bundle.
func (*RecoveryBundle) Interceptors ¶
func (b *RecoveryBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
Interceptors returns the recovery interceptors.
func (*RecoveryBundle) Name ¶
func (b *RecoveryBundle) Name() string
Name returns the bundle identifier.
func (*RecoveryBundle) Priority ¶
func (b *RecoveryBundle) Priority() int
Priority returns the recovery priority (runs last).
type Registrar ¶
type Registrar interface {
RegisterService(server grpc.ServiceRegistrar)
}
Registrar is implemented by gRPC services that want to be auto-discovered and registered with the gRPC server.
Implementations should register themselves with the provided server:
type GreeterService struct {
pb.UnimplementedGreeterServer
}
func (s *GreeterService) RegisterService(server grpc.ServiceRegistrar) {
pb.RegisterGreeterServer(server, s)
}
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a gRPC server with lifecycle management and auto-discovery. It implements di.Starter and di.Stopper for integration with gaz's lifecycle.
func NewServer ¶
func NewServer(cfg Config, logger *slog.Logger, container *di.Container, tp *sdktrace.TracerProvider) *Server
NewServer creates a new gRPC server with the given configuration. The server is not started until OnStart is called.
Interceptors are auto-discovered from the DI container by resolving all implementations of InterceptorBundle. They are chained in order of Priority().
Parameters:
- cfg: Server configuration (port, reflection, message sizes)
- logger: Logger for request logging and error reporting
- container: DI container for service and interceptor discovery
- tp: Optional TracerProvider for OpenTelemetry instrumentation (may be nil)
func (*Server) GRPCServer ¶
GRPCServer returns the underlying grpc.Server for direct access. This is useful for registering services manually if needed.
func (*Server) OnStart ¶
OnStart starts the gRPC server. It binds to the configured port, discovers and registers services, enables reflection if configured, and starts serving in a goroutine. When SkipListener is true, services are registered but no port is bound. Implements di.Starter.
type ValidationBundle ¶
type ValidationBundle struct {
// contains filtered or unexported fields
}
ValidationBundle is the built-in protovalidate interceptor bundle. It validates protobuf messages using buf.build/go/protovalidate rules.
func NewValidationBundle ¶
func NewValidationBundle() (*ValidationBundle, error)
NewValidationBundle creates a new validation interceptor bundle. Returns an error if the validator cannot be created.
func (*ValidationBundle) Interceptors ¶
func (b *ValidationBundle) Interceptors() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor)
Interceptors returns the validation interceptors.
func (*ValidationBundle) Name ¶
func (b *ValidationBundle) Name() string
Name returns the bundle identifier.
func (*ValidationBundle) Priority ¶
func (b *ValidationBundle) Priority() int
Priority returns the validation priority.