grpc

package
v0.0.0-...-4dd1f93 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 4, 2026 License: MIT Imports: 28 Imported by: 0

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

View Source
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.

View Source
const DefaultHealthCheckInterval = 5 * time.Second

DefaultHealthCheckInterval is the default interval for health checks.

View Source
const DefaultMaxMsgSize = 4 * 1024 * 1024

DefaultMaxMsgSize is the default maximum message size (4MB).

View Source
const DefaultPort = 50051

DefaultPort is the default port for the gRPC server.

Variables

This section is empty.

Functions

func InterceptorLogger

func InterceptorLogger(l *slog.Logger) logging.Logger

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

func NewModule() gaz.Module

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.

func (AlwaysPassLimiter) Limit

Limit always returns nil, allowing all requests.

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

Interceptors returns the auth interceptors.

func (*AuthBundle) Name

func (b *AuthBundle) Name() string

Name returns the bundle identifier.

func (*AuthBundle) Priority

func (b *AuthBundle) Priority() int

Priority returns the auth priority (after logging, before validation).

type AuthFunc

type AuthFunc = auth.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 DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with safe defaults.

func (*Config) Flags

func (c *Config) Flags(fs *pflag.FlagSet)

Flags registers the config flags.

func (*Config) Namespace

func (c *Config) Namespace() string

Namespace returns the config namespace.

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.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that the configuration is valid. Implements the config.Validator 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:

  1. Implement this interface
  2. Register the implementation in the DI container
  3. 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

type Limiter = ratelimit.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

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

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

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

func (s *Server) GRPCServer() *grpc.Server

GRPCServer returns the underlying grpc.Server for direct access. This is useful for registering services manually if needed.

func (*Server) OnStart

func (s *Server) OnStart(ctx context.Context) error

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.

func (*Server) OnStop

func (s *Server) OnStop(ctx context.Context) error

OnStop gracefully shuts down the gRPC server. It waits for active connections to complete or forces shutdown on context timeout. When SkipListener is true, GracefulStop is called directly without listener management. Implements di.Stopper.

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

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL