This example demonstrates comprehensive request validation using buf.validate (protovalidate), showcasing all validation patterns available in sebuf.
| Constraint | Description | Example |
|---|---|---|
min_len/max_len |
Length constraints | string name = 1 [(buf.validate.field).string = { min_len: 2, max_len: 100 }] |
email |
Email format | string email = 1 [(buf.validate.field).string.email = true] |
uuid |
UUID format | string id = 1 [(buf.validate.field).string.uuid = true] |
pattern |
Regex pattern | string phone = 1 [(buf.validate.field).string.pattern = "^\\+[1-9]\\d{6,14}$"] |
in |
Enum values | string country = 1 [(buf.validate.field).string = { in: ["US", "CA", "MX"] }] |
| Constraint | Description | Example |
|---|---|---|
gte/lte |
Inclusive range | int32 quantity = 1 [(buf.validate.field).int32 = { gte: 1, lte: 100 }] |
gt/lt |
Exclusive range | double price = 1 [(buf.validate.field).double = { gt: 0, lte: 10000 }] |
| Constraint | Description | Example |
|---|---|---|
min_items/max_items |
Size constraints | repeated Item items = 1 [(buf.validate.field).repeated = { min_items: 1, max_items: 50 }] |
unique |
No duplicates | repeated string codes = 1 [(buf.validate.field).repeated = { unique: true }] |
items |
Element validation | repeated string emails = 1 [(buf.validate.field).repeated = { items: { string: { email: true } } }] |
| Constraint | Description | Example |
|---|---|---|
max_pairs |
Size limit | map<string, string> meta = 1 [(buf.validate.field).map = { max_pairs: 20 }] |
keys |
Key validation | map<string, string> opts = 1 [(buf.validate.field).map = { keys: { string: { pattern: "^[a-z_]+$" } } }] |
values |
Value validation | map<string, string> opts = 1 [(buf.validate.field).map = { values: { string: { max_len: 100 } } }] |
| Constraint | Description | Example |
|---|---|---|
required |
Required nested message | Address shipping = 1 [(buf.validate.field).required = true] |
# Generate code and run the server
make demo
# Test valid requests
make test
# Test validation errors (expect HTTP 400)
make test-validation| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/orders |
Create order with full validation |
| GET | /api/v1/orders/{order_id} |
Get order by UUID |
| POST | /api/v1/validate/address |
Validate shipping address |
| POST | /api/v1/validate/coupon |
Validate coupon code |
curl -X POST http://localhost:8080/api/v1/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: 123e4567-e89b-12d3-a456-426614174000" \
-d '{
"contact": {
"email": "john.doe@example.com",
"phone": "+14155551234"
},
"shipping_address": {
"street": "123 Main Street, Apt 4B",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"items": [{
"product_id": "123e4567-e89b-12d3-a456-426614174000",
"product_name": "Wireless Headphones",
"sku": "SKU-ABC123DEF",
"quantity": 2,
"unit_price": 99.99,
"discount_percent": 10.0,
"options": {"color": "blue", "size": "large"}
}],
"coupon_codes": ["SAVE20"],
"payment_method": 1
}'curl -X POST http://localhost:8080/api/v1/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: 123e4567-e89b-12d3-a456-426614174000" \
-d '{"contact": {"email": "not-an-email"}, ...}'Response (HTTP 400):
{
"violations": [{
"field": "contact.email",
"description": "value must be a valid email address"
}]
}curl -X POST http://localhost:8080/api/v1/validate/address \
-H "Content-Type: application/json" \
-H "X-API-Key: 123e4567-e89b-12d3-a456-426614174000" \
-d '{"address": {"state": "California", ...}}'Response (HTTP 400):
{
"violations": [{
"field": "address.state",
"description": "value does not match pattern: ^[A-Z]{2}$"
}]
}curl -X POST http://localhost:8080/api/v1/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: 123e4567-e89b-12d3-a456-426614174000" \
-d '{"items": [{"quantity": 500, ...}], ...}'Response (HTTP 400):
{
"violations": [{
"field": "items[0].quantity",
"description": "value must be less than or equal to 100"
}]
}curl -X POST http://localhost:8080/api/v1/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: 123e4567-e89b-12d3-a456-426614174000" \
-d '{"items": [], ...}'Response (HTTP 400):
{
"violations": [{
"field": "items",
"description": "value must contain at least 1 item(s)"
}]
}After running make generate:
api/
proto/
models/
order.pb.go # Order, Address, OrderItem messages
services/
order_service.pb.go # Service interface
order_service_http.pb.go # HTTP handler registration
order_service_http_binding.pb.go # Request binding + validation
order_service_http_config.pb.go # Server options
order_service_http_mock.pb.go # Mock implementation
docs/
OrderService.openapi.yaml # OpenAPI 3.1 with validation constraints
OrderService.openapi.json # OpenAPI 3.1 (JSON format)
All validation happens automatically in the generated BindingMiddleware. You don't need to write any validation code - just define the rules in your proto files.
Validation errors return HTTP 400 with a structured response containing:
violations[]: Array of field violationsfield: Path to the invalid field (e.g.,items[0].quantity)description: Human-readable error message
Validation constraints are automatically reflected in the generated OpenAPI specs:
minLength/maxLengthfor string constraintsminimum/maximumfor numeric constraintsminItems/maxItemsfor array constraintspatternfor regex constraintsenumforinconstraints