# protoc-gen-gorm turns a protobuf schema into ORM models and a CRUD API

> protoc-gen-gorm is a protoc plugin that reads a .proto file and writes a second Go file beside the standard generated one, containing GORM models, converters between the protobuf and ORM shapes, and for services marked as autogen a barebones gRPC CRUD server. The opt-in design, with an ormable option per message and an autogen option per service, is what keeps it from generating things you did not ask for.

**infobloxopen/protoc-gen-gorm** — A protobuf compiler plugin designed to generate GORM models and APIs for simple object persistence tasks.

- Repository: https://github.com/infobloxopen/protoc-gen-gorm
- Stars: 627 · Forks: 172
- Language: Go
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/infobloxopen-protoc-gen-gorm

## A second file beside the protobuf one

protoc-gen-gorm is a protobuf compiler plugin designed to simplify the API calls needed to perform simple object persistence tasks, and the way it does that is by generating a companion file rather than by changing the protobuf output.

The mechanism is a secondary .pb.gorm.go file containing sister objects to those generated in the standard .pb.go file. So when you compile a .proto, you get the usual Go protobuf types and, alongside them, ORM-shaped versions of the same things.

The criteria the generated objects satisfy are listed as four points. Go field decorators and tags can be defined by option in the .proto file for GORM and SQL. There are options for dropping fields from the protobuf object, or adding additional fields, which the documentation notes is not generally recommended since it reduces the clarity of the .proto file. Converters between the protobuf version and the ORM version of the objects are included. And hook interfaces allow custom application logic.

That last point is the design decision that makes the whole thing usable rather than a code generator toy. The generated converter is a place where you can intervene, so you can attach your own logic to the boundary between what arrives on the wire and what gets stored.

The project is Go, Apache-2.0 licensed, from Infoblox, with the last push on 2026-04-05 and releases v1.1.5 from 2025-08-26, v0.20.2 from 2024-12-10 and v1.1.4 from 2024-03-13.

## Everything is opt-in, per message and per service

The generation is controlled by options rather than by a global flag, and the two option namespaces are the API you work against.

Any message types with the option (gorm.opts).ormable = true get the generated artefacts. The list is specific. A struct with ORM compatible types and the ORM suffix. GORM tags built from the field options, using the field option syntax with a tag value. A ToORM function and a ToPB function. Additional unexposed fields added from the include option, either of a built-in type such as an int32 named secret_key, or an imported type such as a StringArray named array with the package pointing at a library. And barebones create, update, read, delete and list handlers that accept the protobuf versions, a context, and a gorm.DB, then perform the basic operation on the database with the object.

The context parameter has a stated purpose, used with the multiaccount option and for the collection operators from the related atlas-app-toolkit project.

Services are opt-in separately, with the option (gorm.server).autogen = true. For service methods whose names start with Create, Read, Update or Delete, the generated implementation calls the basic CRUD handlers. For every other method, a stub is generated that returns an empty response and no error.

So the rule is name-based rather than annotation-based. A service method called CreateUser gets a real handler, and one called GetUserProfile gets a stub. That is a simple convention to follow and it is checked automatically.

The service-level option txn_middleware = true makes the generated code integrate with the gRPC server transaction middleware from the atlas-app-toolkit, which is how a generated handler can participate in a transaction that spans several calls.

## The conventions for a generated handler are precise

For CRUD methods to be generated correctly there are specific conventions to follow, and the documentation lists them per method.

For requests, Create and Update methods should have an Ormable Type in a field named payload. Read and Delete methods require an id field. Nothing is required in the List request.

For responses, Create, Read and Update require an Ormable Type in a field named result, and List requires a repeated Ormable Type named results.

Delete methods additionally require the (gorm.method).object_type option, which indicates which Ormable Type it should delete, and Delete has no response type requirements.

The failure mode is stated plainly: if the conventions are not met, stubs are generated for the CRUD methods, and the feature_demo/demo_service example is pointed at as the place where you can see that happening.

That is a reasonable design, because the generator cannot guess. Given a method named DeleteAccount with an account payload and no id, there is more than one sensible interpretation and picking one silently would be worse than refusing. The generator refuses and leaves you a stub, and the stub is visible in the generated code.

There is also a path for customising beyond the generated code, and it is the conventional one for generated servers. You embed the generated type into a new type and override any functions you want to change. So the generated server is a base class rather than a sealed artefact, and the parts you need to be different are the parts you override.

## Setup, and the two ways to import the proto files

There are three prerequisites, and they are the usual ones for a protoc plugin.

The protobuf compiler, protoc, is required, and the documentation links the official installation instructions and an abbreviated version hosted with the gRPC gateway project.

Then the Go protobuf code generator, and the instructions differ by Go version because the go get behaviour changed. Before Go 1.17 the command was `go get -u github.com/golang/protobuf/protoc-gen-go`, and starting from Go 1.17 it is:

```
go install github.com/golang/protobuf/protoc-gen-go@latest
```

Third, the vendored dependencies, retrieved with go mod tidy:

```
go mod tidy
```

Installing the plugin itself has three routes. You can install it from code with make install, install it directly with `go install github.com/infobloxopen/protoc-gen-gorm@latest`, or get it with `go get github.com/infobloxopen/protoc-gen-gorm`.

Once installed, the `--gorm_out=.` option, or `--gorm_out=${GOPATH}src`, is specified in a protoc command to generate the .pb.gorm.go files.

The proto import paths are where people trip, and the documentation handles both ecosystems. With buf you can use short import paths:

```protobuf
import "options/gorm.proto";
import "types/types.proto";
```

and you configure your buf.work.yaml to include the protoc-gen-gorm proto directory, or add it as a dependency in your buf.yaml.

With protoc directly you use the full import paths:

```protobuf
import "github.com/infobloxopen/protoc-gen-gorm/proto/options/gorm.proto";
import "github.com/infobloxopen/protoc-gen-gorm/proto/types/types.proto";
```

and include the path when running protoc:

```bash
protoc -I. -I$GOPATH/src/github.com/infobloxopen/protoc-gen-gorm/proto ...
```

There is also a migration table for the legacy import paths, which were `github.com/infobloxopen/protoc-gen-gorm/options/gorm.proto` and `github.com/infobloxopen/protoc-gen-gorm/types/types.proto`. Backward compatibility is maintained by symlinks at the old paths pointing to the new locations, so existing proto files using the old import paths continue to work.

## Types, database engines and the wrapper types

The type mapping section is where the practical limits of the generator show up.

The standard primitive types are supported and map to the same type at the ORM level: uint32, uint64, int32, int64, float, double, bool and string. Then there are the Google wrapper types, and the list is truncated in the documentation, so the full set of what is mapped is worth checking in the source.

The proto definition files live in the proto/ directory, and there are two of them. `proto/options/gorm.proto` holds the GORM options for messages, fields and services, which is where the option definitions themselves are. And `proto/types/types.proto` holds custom wrapper types, with the examples given being UUID, JSON and Inet.

Those wrapper types are the interesting part of the design, because protobuf has no native representation for a UUID column, a JSON column or an inet column, and a naive generator either refuses those types or maps them to strings and loses the mapping. Declaring them in the proto file means the ORM struct gets the right Go type.

The database engine is chosen at generation time with the `--gorm_out` option using the engine syntax, for example engine={postgres,...}:{path}. The documentation is specific about the scope of that: currently only Postgres has special type support, and any other choice behaves as default.

That is a meaningful limitation to check against your database. If you are on Postgres you get the special handling, and if you are on anything else the generated code uses the default behaviour, which is the standard type mapping and nothing database specific.

The examples directory shows what the output looks like. The user example contains model examples taken from the GORM documentation. The feature_demo/demo_types example demonstrates the type handling and the multi_account functions. And feature_demo/demo_service shows the service autogeneration, including the case where conventions are not met and stubs are produced.

Running make example recompiles all of those test proto files, which is the way to see the effect of changing options and fields without wiring it into your own project.

## Where this fits against writing the persistence layer by hand

The comparison is with hand-written GORM models and a hand-written gRPC service, and the plugin's case is that those two things are derived from the same source anyway.

In a typical protobuf service, the .proto file defines the messages and the service, and protoc produces the Go types and the client and server stubs. Then someone writes the GORM model by hand, writes the conversion between the protobuf struct and the ORM struct, and writes the CRUD handler by hand. Three artefacts, two of which restate the first.

That restatement is where drift comes from. Add a field to the proto file, regenerate, and the hand-written model and converter are now out of date, and nothing tells you. The compiler will not complain, because the protobuf type still compiles. The failure appears at runtime as a missing column or a silently dropped field.

Generating the model and the converter from the same schema removes that failure mode, which is the real argument for the plugin rather than the amount of typing it saves.

The counterargument is the one the documentation makes about the include option, that adding extra fields is not generally recommended since it reduces the clarity of the .proto file. That is the trade in a single sentence. If your ORM model needs something the wire format does not have, you can declare it, but now your proto file is no longer purely a wire contract and the generator has become part of your domain modelling rather than a convenience.

For a service where the domain and the wire format are close, which is common for internal APIs and CRUD-shaped services, that is a reasonable place to be. For a service with a rich domain model, the generated ORM shape will be too thin and you will end up hand-writing the interesting part anyway.

The barebones handler description tells you what you get. The generated handlers accept the protobuf version, a context and a gorm.DB, and perform the basic operation. That is a working endpoint, not a designed one, and the embed-and-override path is how you take it further.

## Conclusion

Adopt protoc-gen-gorm if your Go service already speaks protobuf over gRPC and you want the persistence layer to come from the same schema, since the converters and the CRUD server are generated from the same definitions your API already uses. Skip it if your domain model is richer than your wire format, because the ORM shape is a sister of the protobuf shape and extra fields are explicitly marked as reducing clarity, and if you want more than barebones CRUD, since every generated handler does one thing. Verify first which database engine you target, because only Postgres has special type support and any other choice behaves as default, that your service methods follow the naming and field conventions, since a method that breaks them gets a stub instead of a handler, and which import path style you use, because the proto paths moved and the old ones are kept only through symlinks.

## FAQ

### What does protoc-gen-gorm generate from a .proto file?

For any message with the option (gorm.opts).ormable = true it generates a struct with ORM compatible types and the ORM suffix, GORM tags built from the field options, ToORM and ToPB converter functions, additional unexposed fields from the include option, and barebones create, update, read, delete and list handlers. For any service with (gorm.server).autogen = true it generates a gRPC server where methods named Create, Read, Update or Delete call the CRUD handlers and all others get stubs.

### How do I install and run protoc-gen-gorm?

Install it from code with make install, with `go install github.com/infobloxopen/protoc-gen-gorm@latest`, or with go get. You also need protoc, the Go protobuf code generator installed with `go install github.com/golang/protobuf/protoc-gen-go@latest` on Go 1.17 and later, and `go mod tidy` for the vendored dependencies. Then pass `--gorm_out=.` to your protoc command.

### Which database engines does protoc-gen-gorm support?

The engine is chosen at generation time with the `--gorm_out` option in the form engine={postgres,...}:{path}. Currently only Postgres has special type support, and any other choice behaves as default. The proto files also define custom wrapper types in proto/types/types.proto, with UUID, JSON and Inet given as the examples.

## Sources

- [infobloxopen/protoc-gen-gorm on GitHub](https://github.com/infobloxopen/protoc-gen-gorm)
- [Issues](https://github.com/infobloxopen/protoc-gen-gorm/issues)
- [License: Apache-2.0](https://github.com/infobloxopen/protoc-gen-gorm/blob/main/LICENSE)
- [README](https://github.com/infobloxopen/protoc-gen-gorm/blob/main/README.md)
- [Releases](https://github.com/infobloxopen/protoc-gen-gorm/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/infobloxopen-protoc-gen-gorm
