Protoc plugin to generate polyglot message validators for protocol buffers
—
Runtime libraries provide language-specific utilities, validation support, and integration helpers for protoc-gen-validate generated code. Each target language has different runtime requirements and capabilities.
Go generated code has zero runtime dependencies and integrates directly with existing protobuf Go libraries.
Generated validation methods are added directly to protobuf message types:
// No additional runtime library required
// Methods are generated directly on message types
func (m *Message) Validate() error
func (m *Message) ValidateAll() errorUses standard Go error interface with contextual error messages:
// Example error patterns generated in validation code
return fmt.Errorf("invalid %s.%s: %s", messageType, fieldName, constraint)While no runtime library is required, common integration patterns include:
// HTTP middleware example (user-implemented)
func ValidateRequest(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req proto.Message
// ... decode request
if err := req.(interface{ Validate() error }).Validate(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
next.ServeHTTP(w, r)
})
}Java runtime provides validator discovery, caching, and gRPC integration through the pgv-java-stub library.
class ReflectiveValidatorIndex implements ValidatorIndex {
public Validator validatorFor(Class<?> clazz);
}Reflection-based validator discovery and caching.
Methods:
validatorFor(Class<?> clazz): Returns cached validator for message classUsage:
ValidatorIndex index = new ReflectiveValidatorIndex();
Validator<UserProto> validator = index.validatorFor(UserProto.class);
validator.assertValid(user);interface Validator<T> {
void assertValid(T message) throws ValidationException;
}Base interface implemented by all generated validators.
class ValidationException extends Exception {
public ValidationException(String message);
public String getFieldPath();
public Object getInvalidValue();
}Exception thrown by validators on validation failure.
class ValidatingClientInterceptor implements ClientInterceptor {
public ValidatingClientInterceptor(ValidatorIndex index);
}Client-side gRPC interceptor for automatic request/response validation.
class ValidatingServerInterceptor implements ServerInterceptor {
public ValidatingServerInterceptor(ValidatorIndex index);
}Server-side gRPC interceptor for automatic request/response validation.
Usage:
// Client setup
ValidatorIndex index = new ReflectiveValidatorIndex();
ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 8080)
.usePlaintext()
.build();
UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc.newBlockingStub(channel)
.withInterceptors(new ValidatingClientInterceptor(index));
// Server setup
Server server = ServerBuilder.forPort(8080)
.addService(ServerInterceptors.intercept(
new UserServiceImpl(),
new ValidatingServerInterceptor(index)
))
.build();<dependency>
<groupId>build.buf.protoc-gen-validate</groupId>
<artifactId>pgv-java-stub</artifactId>
<version>${pgv.version}</version>
</dependency>Python provides a full runtime library for JIT validation function generation and caching.
# protoc_gen_validate.validator moduleMain module containing validation functionality.
def validate(proto_message: Message)Main validation function that dynamically generates and executes validation code.
Parameters:
msg: Protobuf message instance to validateRaises: ValidationFailed when validation fails
Implementation:
exec() to compile and execute validation logicdef validate_all(proto_message: Message)Validates message and returns all validation errors instead of raising exception on first error.
Parameters:
proto_message: Message: Protocol buffer message to validateRaises: ValidationFailed with all validation error messages
def print_validate(msg) -> NoneDebug utility that prints generated validation code.
Parameters:
msg: Protobuf message instanceBehavior:
validate()class ValidatingMessage(object):
"""Wrap a proto message to cache validate functions"""
def __init__(self, proto_message: Message)
def __hash__(self) -> int
def __eq__(self, other) -> bool
class ValidationFailed(Exception):
"""Raised when message validation fails"""
passException raised when validation fails.
Usage:
from protoc_gen_validate.validator import validate, ValidationFailed
try:
validate(message)
except ValidationFailed as e:
logger.error(f"Validation failed: {e}")# Internal caching mechanism (implementation detail)
@lru_cache(maxsize=128)
def _get_validator_func(descriptor_name):
# Returns cached validation function
passPerformance optimizations:
pip install protoc-gen-validateC++ generated code has minimal runtime dependencies and uses standard library types.
namespace validate {
bool Validate(const Message& msg, std::string* error = nullptr);
}Generated validation functions follow consistent pattern:
bool for validation resultstd::string* parameter for error detailsC++ runtime requires only standard library and protobuf headers:
#include <string>
#include <google/protobuf/message.h>
// Generated validation headers// Error handling pattern
std::string error;
if (!validate::Validate(message, &error)) {
// Handle validation failure
std::cerr << "Validation failed: " << error << std::endl;
}// Service layer integration
class UserService {
public:
Status CreateUser(const CreateUserRequest& request) {
std::string validation_error;
if (!validate::Validate(request, &validation_error)) {
return Status(StatusCode::kInvalidArgument, validation_error);
}
// Process valid request
return Status::OK;
}
};| Feature | Go | Java | C++ | Python |
|---|---|---|---|---|
| Runtime Dependencies | None | pgv-java-stub | Standard library | protoc-gen-validate |
| Error Types | error interface | ValidationException | std::string | ValidationFailed |
| Performance | Zero allocation | Reflection cache | Static validation | JIT with cache |
| gRPC Integration | Manual | Built-in interceptors | Manual | Manual |
| Framework Integration | Manual | Spring/JAX-RS ready | Manual | Django/Flask ready |
All runtimes maintain:
Go:
# No runtime installation needed
# Generated code has zero dependenciesJava:
<!-- Maven -->
<dependency>
<groupId>build.buf.protoc-gen-validate</groupId>
<artifactId>pgv-java-stub</artifactId>
<version>${pgv.version}</version>
</dependency>C++:
# No runtime installation needed
# Generated code uses standard libraryPython:
pip install protoc-gen-validateexec() overheadAll runtimes support:
Language-specific development aids:
Install with Tessl CLI
npx tessl i tessl/go-protoc-gen-validate