puregen is a protobuf plugin that generates simple, dependency-minimal code for Go, Java, and Python from .proto
files. The generated code focuses on simplicity and uses built-in language features rather than heavy dependencies.
puregen is ideal for projects that need simple, readable generated code without heavy protobuf runtime dependencies, with the flexibility to use any transport mechanism (HTTP, gRPC, message queues, etc.).
Feature | puregen | protoc-gen-go | protoc-gen-java |
---|---|---|---|
Dependencies | Minimal | protobuf runtime | protobuf runtime |
Code size | Small | Large | Large |
JSON support | Built-in | Requires jsonpb | Requires additional libs |
Transport abstraction | Pluggable | gRPC only | gRPC only |
Customization | Easy | Complex | Complex |
Learning curve | Low | Medium | Medium |
- Multi-language support: Generate code for Go, Java, and Python
- Minimal dependencies: Uses only built-in libraries and standard patterns
- Simple data structures: Generated classes/structs are easy to understand and modify
- JSON serialization: Built-in JSON marshaling/unmarshaling support
- Service interfaces: Clean interface definitions for RPC services
- Method metadata support: Extract metadata from service method comments (//metadata:{...}) for HTTP routing, authorization, etc. See details
- Client generation: Ready-to-use clients with pluggable transport. See details
- Protocol Buffers compiler (
protoc
). Install it from the official site.
You can download pre-built binaries for your platform from the releases page.
curl -L https://github.com/nnanto/puregen/releases/download/latest/protoc-gen-puregen-linux-amd64.tar.gz | tar -xz
sudo mv protoc-gen-puregen-* /usr/local/bin/protoc-gen-puregen
# Or for macOS
curl -L https://github.com/nnanto/puregen/releases/download/latest/protoc-gen-puregen-darwin-amd64.tar.gz | tar -xz
sudo mv protoc-gen-puregen-* /usr/local/bin/protoc-gen-puregen
# Run the generator
protoc --puregen_out=./examples/generated --puregen_opt=language=python examples/proto/*.proto
# Clone the repository
git clone https://github.com/nnanto/puregen
cd puregen
# Build the plugin
go build -o protoc-gen-puregen ./cmd/protoc-gen-puregen
# Make it available in your PATH (optional)
sudo mv protoc-gen-puregen /usr/local/bin/
Generate code for all supported languages:
protoc --puregen_out=./generated --puregen_opt=language=all user.proto
Generate code for a specific language:
# Go only
protoc --puregen_out=./generated --puregen_opt=language=go user.proto
# Java only
protoc --puregen_out=./generated --puregen_opt=language=java user.proto
# Python only
protoc --puregen_out=./generated --puregen_opt=language=python user.proto
syntax = "proto3";
package example.v1;
option go_package = "github.com/nnanto/puregen/examples/proto/gen/go";
option java_package = "com.example.proto.v1";
// User represents a user in the system
message User {
int32 id = 1;
string name = 2;
string email = 3;
bool is_active = 4;
UserProfile profile = 5;
}
// UserProfile contains additional user information
message UserProfile {
string bio = 1;
string avatar_url = 2;
int64 created_at = 3;
}
// CreateUserRequest is the request message for creating a user
message CreateUserRequest {
string name = 1;
string email = 2;
UserProfile profile = 3;
}
message CreateUserResponse {
User user = 1;
string message = 2;
}
message GetUserRequest {
int32 id = 1;
}
message GetUserResponse {
User user = 1;
}
service UserService {
// CreateUser creates a new user
// metadata: {"method":"POST", "path":"/users"}
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
// GetUser retrieves a user by ID
// metadata: {"method":"GET", "path":"/users/{id}"}
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
Simple data structures with validation and serialization
- Go Models Example - Creating and working with generated Go structs
- Java Models Example - Using generated Java classes with builder pattern
- Python Models Example - Working with generated Python dataclasses
Service implementations with HTTP endpoints
- Go Server Example - HTTP server implementation with generated service interface
- Java Server Example - Java HTTP server using generated service classes
- Python Server Example - Flask-based server with generated service interface
Client libraries with pluggable transport
- Go Client Example - HTTP client with custom transport implementation
- Java Client Example - Java HTTP client with transport abstraction
- Python Client Example - Python client with requests-based transport
You can define custom transports for different protocols (HTTP, gRPC, etc.) by implementing the Transport
interface in each language.
Example: Name-Based Routing Transport
The generator supports extracting metadata from service method comments. This is useful for HTTP routing, authorization, and other transport-specific configurations.
Add metadata to method comments using the metadata:
prefix followed by a JSON object:
service UserService {
// metadata:{"method":"GET", "path":"/users/{id}", "auth":"required"}
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// metadata:{"method":"POST", "path":"/users", "auth":"required", "role":"admin"}
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
// metadata:{"method":"DELETE", "path":"/users/{id}", "auth":"required", "role":"admin"}
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
}
Go:
// Access metadata using method constants
metadata := MethodMetadata[UserService_GetUser]
httpMethod := metadata["method"] // "GET"
path := metadata["path"] // "/users/{id}"
auth := metadata["auth"] // "required"
Java:
// Access metadata through the Methods class
Map<String, String> metadata = UserServiceMethods.METHOD_METADATA.get(UserServiceMethods.UserService_GetUser);
String httpMethod = metadata.get("method"); // "GET"
String path = metadata.get("path"); // "/users/{id}"
String auth = metadata.get("auth"); // "required"
Python:
# Access metadata through the Methods class
metadata = UserServiceMethods.METHOD_METADATA[UserServiceMethods.UserService_GetUser]
http_method = metadata["method"] # "GET"
path = metadata["path"] # "/users/{id}"
auth = metadata["auth"] # "required"
- HTTP Routing: Use
method
andpath
metadata for automatic route registration - Authentication: Use
auth
metadata to determine if authentication is required - Authorization: Use
role
metadata for role-based access control - Rate Limiting: Add custom metadata for rate limiting configurations
- Documentation: Include API versioning or documentation URLs
- Struct definitions with JSON tags
- Constructor functions (
NewMessageName()
) - Validation methods
- JSON serialization (
ToJSON()
,FromJSON()
) - Service interfaces with default implementations
- Clients with pluggable Transport interface
- POJO classes with Jackson annotations
- Builder pattern support
- Getters and setters
- JSON serialization methods
- Service interfaces with default implementations
- Clients with generic Transport interface
- Dataclasses with type hints
- JSON serialization support
- Validation methods
- Service abstract base classes
- Clients with abstract Transport base class
Test with the provided example:
# Generate code for the example proto file
cd examples
protoc --puregen_out=./generated --puregen_opt=language=all proto/user.proto
# Check the generated files
ls -la generated/
├── cmd/protoc-gen-puregen/ # Main plugin entry point
├── internal/generator/ # Code generation logic
│ ├── go.go # Go code generator
│ ├── java.go # Java code generator
│ └── python.go # Python code generator
├── examples/ # Example proto files and usage
└── README.md
- Create a new generator file in
internal/generator/
- Implement the
GenerateXXXFile
function - Add the language option to the main plugin
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
MIT License - see LICENSE file for details.