Generate code with the Buf CLI#
This walkthrough produces Go stubs for a small weather API three different ways: with a locally installed protoc-gen-go binary, with the same plugin run remotely on the Buf Schema Registry (BSR), and with managed mode controlling Go’s go_package option from buf.gen.yaml instead of the .proto file.
Before you start#
- Install the Buf CLI.
-
Install
protoc-gen-goand put it on$PATH.local:plugins are invoked directly;protocitself isn’t needed for this walkthrough.
Set up the workspace#
A Buf workspace is a directory tree containing one buf.yaml, with one or more modules underneath it.
Create the workspace and a default buf.yaml:
buf config init writes a minimal buf.yaml:
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE
Add a modules entry pointing at the proto/ directory you’ll create in the next step:
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
+modules:
+ - path: proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
For the full set of buf.yaml keys, see the buf.yaml reference.
Add a Protobuf file#
syntax = "proto3";
package acme.weather.v1;
option go_package = "acme/weather/v1";
enum Condition {
CONDITION_UNSPECIFIED = 0;
CONDITION_SUNNY = 1;
CONDITION_RAINY = 2;
}
message GetWeatherRequest {
float latitude = 1;
float longitude = 2;
}
message GetWeatherResponse {
float temperature = 1;
Condition condition = 2;
}
service WeatherService {
rpc GetWeather (GetWeatherRequest) returns (GetWeatherResponse);
}
Configure code generation#
Create buf.gen.yaml next to buf.yaml:
buf-codegen-quickstart
├── buf.gen.yaml
├── buf.yaml
└── proto
└── acme
└── weather
└── v1
└── weather.proto
version: v2
clean: true
plugins:
- local: protoc-gen-go
out: gen/go
opt: paths=source_relative
inputs:
- directory: proto
clean: true deletes the previous contents of each out directory before generation runs, so removed .proto types don’t leave behind stale generated files.
local: protoc-gen-go runs the binary on your $PATH.
For the full configuration surface, see the buf.gen.yaml reference.
Generate with a local plugin#
A new gen/ directory appears, mirroring the source directory structure:
buf-codegen-quickstart
├── buf.gen.yaml
├── buf.yaml
├── gen
│ └── go
│ └── acme
│ └── weather
│ └── v1
│ └── weather.pb.go
└── proto
└── acme
└── weather
└── v1
└── weather.proto
Switch to a remote plugin#
The same protoc-gen-go plugin is hosted on the BSR.
Pointing at the BSR copy removes the local install requirement so anyone running buf generate gets the same plugin version without managing their own toolchain.
Remove the previous output and update buf.gen.yaml to use the remote plugin:
version: v2
clean: true
plugins:
- - local: protoc-gen-go
+ - remote: buf.build/protocolbuffers/go:v1.36.11
out: gen/go
opt: paths=source_relative
inputs:
- directory: proto
Regenerate:
The output tree is identical to the local-plugin run. For details on the remote-plugin model, see Remote plugins. If the generated code is mainly for downstream consumers rather than this repository, publish the module to the BSR and let consumers install generated SDKs instead.
Enable managed mode#
Managed mode lets buf.gen.yaml set the language-specific file options instead of the producer hard-coding them in every .proto file. Drop the go_package declaration from weather.proto first:
syntax = "proto3";
package acme.weather.v1;
-
-option go_package = "acme/weather/v1";
enum Condition {
Suppose your code lives at github.com/acme/weather, and the generated Go must import as github.com/acme/weather/gen/go/acme/weather/v1.
Set go_package_prefix in the managed block:
version: v2
clean: true
+managed:
+ enabled: true
+ override:
+ - file_option: go_package_prefix
+ value: github.com/acme/weather/gen/go
plugins:
- remote: buf.build/protocolbuffers/go:v1.36.11
out: gen/go
opt: paths=source_relative
inputs:
- directory: proto
Run buf generate again.
The output directory is unchanged: paths=source_relative keeps files mirroring the source tree.
What changes is the go_package value the plugin sees.
For a versioned package like acme.weather.v1, managed mode produces <prefix>/acme/weather/v1;weatherv1 (the final ;name segment lets the generated Go package use a short name):
syntax = "proto3";
package acme.weather.v1;
option go_package = "github.com/acme/weather/gen/go/acme/weather/v1;weatherv1";
// Messages, enums, services, etc.
The generated Go file imports as github.com/acme/weather/gen/go/acme/weather/v1, matching what the consumer’s import paths expect.
Next steps#
- Managed mode: the full set of file and field options managed mode can control.
- Remote plugins: how plugin execution on the BSR works.
- Generated SDKs: package-manager installs for code generated from published BSR modules.
buf.gen.yamlreference: every configuration key.