English | 中文
Cato is a protobuf-driven code generator for Go server projects. It keeps storage metadata, HTTP routing, request description, and model generation rules close to your .proto files, then generates the repetitive layers around them.
Warning
Cato is still under active development. Breaking changes can happen, and the current version should be evaluated carefully before production use.
- Define server-side structure once in protobuf and reuse it across models, repo interfaces, RDB implementations, and HTTP services.
- Generate code that is repetitive but easy to drift by hand: table metadata, CRUD skeletons, route registration, request docs, and struct tags.
- Keep manual extension points safe. Files ending in
.cato.goare regenerated every time, while*_custom.go,*_extend.go, andextension.goare only created when missing.
- Model files with struct fields, tags, table helpers, column groups, JSON conversion helpers, and time-format helpers.
- Repo interfaces and constructors backed by the generic
core/rdb.Engine. - Default RDB implementations that currently target the
core/rdbabstractions and ship with anxormadapter. - HTTP service interfaces, handler registration scaffolding, and OpenAPI 2.0
swagger.json. - Shared protobuf option definitions under
protoand their generated Go bindings undergenerated.
- Go 1.24 or newer
protocprotoc-gen-go
go install github.com/ncuhome/cato/cmd/protoc-gen-cato@latestOr from this repository:
make installIf your generated project imports Cato runtime helpers, add the module dependency there as well:
go get github.com/ncuhome/cato@latestCato's custom options are imported as cato/proto/*.proto, so your protoc include path must point to the parent directory that contains the cato folder.
One workable layout is:
third_party/
cato/
proto/
extension.proto
db.proto
defines.proto
http.proto
struct.proto
With that layout, pass -I ./third_party to protoc.
At file level, Cato currently uses these package options:
cato_package: package for generated model files and HTTP service scaffoldingrepo_package: package for generated repo interfacesrdb_repo_package: package for generated default RDB implementations
Example:
syntax = "proto3";
package example.user.v1;
option go_package = "github.com/your-org/your-project/api/user/v1;userv1";
import "cato/proto/extension.proto";
option (cato.cato_opt) = {
cato_package: "internal/model/user"
repo_package: "internal/repo/user"
rdb_repo_package: "internal/repo/user/rdb"
};
message User {
option (cato.db_opt) = {
db_type: CATO_DB_TYPE_MYSQL
};
option (cato.table_opt) = {
name_option: { simple_name: "users" }
};
option (cato.struct_opt) = {
field_default_tags: [
{
tag_name: "json"
tag_value: "%s,omitempty"
mapper: CATO_FIELD_MAPPER_SNAKE_CASE
}
]
};
int64 id = 1 [(cato.column_opt) = {
col_desc: {
field_name: "id"
comment: "primary key"
}
keys: [{ key_name: "PRIMARY", key_type: CATO_DB_KEY_TYPE_PRIMARY }]
}];
string nickname = 2 [(cato.column_opt) = {
col_desc: {
field_name: "nickname"
comment: "display name"
}
}];
}
message CreateUserRequest {
option (cato.http_param_opt) = {};
string nickname = 1 [(cato.http_pf_opt) = {
name: "nickname"
must: true
example: "neo"
}];
}
message CreateUserResponse {
User user = 1;
}
service UserService {
option (cato.http_opt) = {
group_prefix: "/v1/users"
as_http_service: true
};
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse) {
option (cato.router_opt) = {
router: "/create"
method: "POST"
};
}
}protoc \
-I . \
-I ./third_party \
--go_out=. \
--go_opt=paths=source_relative \
--cato_out=. \
--cato_opt=ext_out_dir=.,swagger_path=./swagger.json,api_host=localhost \
api/user/v1/user.proto--cato_opt currently supports:
| Flag | Description |
|---|---|
ext_out_dir |
Root directory used to check whether custom files already exist. In practice this should usually match --cato_out and point at your Go module root. |
swagger_path |
Optional output path for generated OpenAPI 2.0 JSON. |
api_host |
Optional host value written into swagger.json. |
Running Cato can produce files like:
<proto>.cato.go: shared model output for the protobuf file<message>_repo.cato.go: generated repo interface and constructor<message>_rdb.cato.go: generated default RDB implementation<message>_extend.go: model extension file, created once and preservedextension.go: repo extension interface, created once and preservedhandlers.cato.go: generated HTTP handler registrationhandlers_custom.go: custom HTTP extension file, created once and preservedapi.cato.go: generated service interface bootstrapapi_custom.go: custom service extension file, created once and preserved
The intended workflow is simple: regenerate .cato.go files whenever your proto changes, and put handwritten logic into the preserved extension files.
Generated code currently imports helper packages from this repository:
core/rdb: generic repo engine abstraction and a built-inxormimplementationcore/httpc: HTTP service abstraction and handler container utilities
The repository also contains core/param, a small request/response binder abstraction that can be used by surrounding application code, although the current templates do not wire it automatically.
cmd/protoc-gen-cato/ protoc plugin entry
proto/ protobuf option definitions exposed to users
generated/ generated Go code for custom protobuf options
src/ generator implementation and option handlers
core/ runtime helpers used by generated code
config/templates/ code generation templates
- Follow the protobuf official style guide. Field and message naming outside the usual conventions can lead to awkward generated identifiers.
- Cato already defines DDL-related protobuf options, but migration/DDL generation is not complete yet.
- Current runtime integrations are centered on repo generation, HTTP scaffolding, and the
xorm-backed RDB adapter. swagger.jsongeneration is OpenAPI 2.0, not OpenAPI 3.
For a fuller example of how these options are used in practice, see cato-example-bms.
- Improve RDB and repo generation.
- Support enum-oriented generation.
- Complete DDL and migration support.
- Continue simplifying generator context and extensibility.