diff --git a/Index.md b/Index.md index bb5afa8..767097c 100644 --- a/Index.md +++ b/Index.md @@ -38,6 +38,7 @@ A Kubernetes-native Go microservice framework for building production-grade gRPC | **gRPC Reflection** | Server reflection enabled by default — works with [grpcurl], [grpcui], and Postman | | **HTTP Compression** | Automatic gzip and [zstd] compression for all HTTP gateway responses (content-negotiated via `Accept-Encoding`) | | **Container-aware Runtime** | Auto-tunes GOMAXPROCS to match container CPU limits via [automaxprocs] | +| **Request Validation** | [Protovalidate] annotations enforced automatically on both gRPC and HTTP requests — define validation rules in your proto, get `InvalidArgument` errors for free | | **CI/CD Pipelines** | Ready-to-use [GitHub Actions] and [GitLab CI] workflows for build, test, lint, coverage, and benchmarks | ## Quick Start @@ -100,7 +101,7 @@ Run `buf generate` — it creates typed Go interfaces from your proto definition │ │ Gateway │──► │ │ │ │ │ (grpc- │ │ ► Response Time │ │ gRPC Request ──► │ │ gateway)│ │ ► Trace ID │ │ - │ └─────────┘ │ ► OpenTelemetry │ │ + │ └─────────┘ │ ► Proto Validate │ │ │ │ │ ► Prometheus │ │ │ ▼ │ ► Error Notify │ │ │ ┌─────────┐ │ ► Panic Recovery │ │ @@ -188,3 +189,4 @@ ColdBrew composes proven Go libraries — not replacements: [GitLab CI]: https://docs.gitlab.com/ci/ [slog]: https://pkg.go.dev/log/slog [zstd]: https://datatracker.ietf.org/doc/html/rfc8878 +[Protovalidate]: https://github.com/bufbuild/protovalidate diff --git a/architecture.md b/architecture.md index b78cc9c..87e12c6 100644 --- a/architecture.md +++ b/architecture.md @@ -154,10 +154,11 @@ When a request arrives at a ColdBrew service, it flows through several layers: │ │ │ │ │ │ 1. Response Time Logging │ │ │ │ 2. Trace ID Injection │ │ - │ │ 3. Prometheus Metrics │ │ - │ │ 4. Error Notification (Sentry/Rollbar) │ │ - │ │ 5. New Relic Transaction │ │ - │ │ 6. Panic Recovery │ │ + │ │ 3. Proto Validate │ │ + │ │ 4. Prometheus Metrics │ │ + │ │ 5. Error Notification (Sentry/Rollbar) │ │ + │ │ 6. New Relic Transaction │ │ + │ │ 7. Panic Recovery │ │ │ │ (OTEL tracing via gRPC stats handler) │ │ │ │ │ │ │ └────────────────────┬─────────────────────┘ │ @@ -200,10 +201,11 @@ Interceptors are gRPC middleware that run on every request. ColdBrew chains them |-------|------------|---------|--------------| | 1 | Response Time Logging | `interceptors` | Logs method name, duration, and status code | | 2 | Trace ID | `interceptors` | Generates a trace ID (or reads it from the `x-trace-id` HTTP header or a `trace_id` proto field) and propagates it to structured logs, Sentry/Rollbar error reports, and OpenTelemetry spans (as the `coldbrew.trace_id` attribute) | -| 3 | Prometheus | `interceptors` | Records request count, latency histogram, and status codes | -| 4 | Error Notification | `interceptors` | Sends errors to Sentry/Rollbar/Airbrake asynchronously | -| 5 | New Relic | `interceptors` | Creates a New Relic transaction for APM | -| 6 | Panic Recovery | `interceptors` | Catches panics and converts them to gRPC errors | +| 3 | Proto Validate | `interceptors` | Validates incoming messages using [protovalidate](https://github.com/bufbuild/protovalidate) annotations. Returns `InvalidArgument` on failure. Disable with `DISABLE_PROTO_VALIDATE` | +| 4 | Prometheus | `interceptors` | Records request count, latency histogram, and status codes | +| 5 | Error Notification | `interceptors` | Sends errors to Sentry/Rollbar/Airbrake asynchronously | +| 6 | New Relic | `interceptors` | Creates a New Relic transaction for APM | +| 7 | Panic Recovery | `interceptors` | Catches panics and converts them to gRPC errors | {: .note } OpenTelemetry tracing spans are created by the `otelgrpc` stats handler configured at the gRPC server/client level, not as an interceptor in the chain. diff --git a/config-reference.md b/config-reference.md index 0a79338..b7074b2 100644 --- a/config-reference.md +++ b/config-reference.md @@ -51,6 +51,7 @@ cfg := config.GetColdBrewConfig() | `GRPC_MAX_SEND_MSG_SIZE` | int | `2147483647` | Maximum send message size in bytes (default: ~2GB, unlimited) | | `GRPC_MAX_RECV_MSG_SIZE` | int | `4194304` | Maximum receive message size in bytes (default: 4MB) | | `DISABLE_VT_PROTOBUF` | bool | `false` | Disable [vtprotobuf](https://github.com/planetscale/vtprotobuf) marshaller for gRPC. See [vtprotobuf guide](/howto/vtproto) | +| `DISABLE_PROTO_VALIDATE` | bool | `false` | Disable [protovalidate](https://github.com/bufbuild/protovalidate) interceptor. When disabled, proto validation annotations are ignored | ## gRPC TLS diff --git a/howto/interceptors.md b/howto/interceptors.md index 2e95227..11d7116 100644 --- a/howto/interceptors.md +++ b/howto/interceptors.md @@ -134,6 +134,42 @@ func main() { } ``` +## Proto Validation + +ColdBrew includes a [protovalidate](https://github.com/bufbuild/protovalidate) interceptor in the default chain. It validates incoming messages using annotations defined in your `.proto` files and returns `InvalidArgument` on failure. Validation runs on both gRPC and HTTP gateway requests — the HTTP gateway translates to gRPC internally, so the interceptor covers both transports from a single annotation. + +### Adding validation rules + +First, add `buf.build/bufbuild/protovalidate` to your `buf.yaml` deps and run `buf dep update`. Then add annotations to your proto: + +```protobuf +import "buf/validate/validate.proto"; + +message CreateUserRequest { + string email = 1 [(buf.validate.field).string.email = true]; + string name = 2 [(buf.validate.field).string.min_len = 1]; + int32 age = 3 [(buf.validate.field).int32 = {gte: 0, lte: 150}]; +} +``` + +No code changes needed — the interceptor validates automatically. + +### Custom constraints + +Add custom validation options during `init()`: + +```go +func init() { + interceptors.SetProtoValidateOptions( + protovalidate.WithCustomConstraints(myConstraints...), + ) +} +``` + +### Disabling + +Set `DISABLE_PROTO_VALIDATE=true` to skip validation entirely. + ## Adding custom interceptors to Default interceptors You can add your own interceptors to the [Default Interceptors] by appending to the list of interceptors. diff --git a/quickstart.md b/quickstart.md index 7eb7256..0525919 100644 --- a/quickstart.md +++ b/quickstart.md @@ -208,8 +208,10 @@ Let's add a `Greet` endpoint to your service. Add to `proto/echoserver.proto`: ```protobuf +import "buf/validate/validate.proto"; + message GreetRequest { - string name = 1; + string name = 1 [(buf.validate.field).string.min_len = 1]; } message GreetResponse { @@ -217,6 +219,8 @@ message GreetResponse { } ``` +The `min_len = 1` annotation ensures the name is not empty. ColdBrew validates this automatically on both gRPC and HTTP requests — sending an empty name returns `InvalidArgument`. + Add the RPC method to your service block: ```protobuf @@ -358,6 +362,7 @@ Everything below was set up automatically by ColdBrew: - **Distributed tracing** support (OpenTelemetry, Jaeger, New Relic) - **Prometheus metrics** for every gRPC method (latency, error rate, in-flight) - **gRPC interceptors** for logging, tracing, metrics, error notification, panic recovery +- **Request validation** via [protovalidate](https://github.com/bufbuild/protovalidate) — define rules in proto, enforced automatically on both gRPC and HTTP requests - **Health checks** for Kubernetes liveness/readiness probes - **Graceful shutdown** on SIGTERM/SIGINT (Kubernetes pod termination) - **pprof profiling** endpoints for debugging