Skip to content

The usual way to work with Protobuf is to generate code. You run a compiler over your .proto files, and plugins emit the types, binary serializers, JSON mapping, and RPC stubs for your language.

proto/intro/v1/user.proto
edition = "2024";

package intro.v1;

message User {
  int64 id = 1;
  string name = 2;
  uint32 age = 3;
  float height_cm = 4;
  double weight_kg = 5;
  bool verified = 6;
}

What you get

Once code is generated from this schema, you can create User objects in your language with type checking and editor support, and convert them to compact binary or JSON for sending or storing.

Generating Code with buf

This guide uses the Buf CLI, which keeps generation declarative with a buf.gen.yaml file. Checking it into your repo lets everyone on the team generate the same output with one command. The remote plugins in this example run on the Buf Schema Registry, so there are no plugin binaries to install locally, and pinning their versions keeps everyone's output identical. The buf.yaml beside it marks proto/ as the root of the schema files. Go code also needs an import path for each file; managed mode fills in go_package from the prefix, so you don't have to set it in the schema.

buf.yaml
version: v2
modules:
  - path: proto
buf.gen.yaml
version: v2
managed:
  enabled: true
  override:
    - file_option: go_package_prefix
      value: example.com/gen/go
plugins:
  - remote: buf.build/bufbuild/es:v2.14.0
    out: web/src/gen
    opt: target=ts
  - remote: buf.build/protocolbuffers/go:v1.36.12
    out: gen/go
    opt: paths=source_relative
GENERATE CODE
$ buf generate

Generating with protoc

protoc is the reference compiler for Protobuf. Plugins (like protoc-gen-es) are executables that protoc finds on your PATH, or at the path you give it with --plugin. Without managed mode, the Go import path comes from a go_package option in the file or an M flag on the command line. protoc creates the folders inside each output directory but not the output directory itself, so make those first.

Install plugins
$ npm install @bufbuild/protobuf
$ npm install --save-dev @bufbuild/protoc-gen-es
$ go get google.golang.org/protobuf@v1.36.12
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.12
Generate code
$ mkdir -p web/src/gen gen/go
$ protoc -I proto \
    --plugin=protoc-gen-es=node_modules/.bin/protoc-gen-es \
    --es_out=web/src/gen --es_opt=target=ts \
    --go_out=gen/go --go_opt=paths=source_relative \
    --go_opt=Mintro/v1/user.proto=example.com/gen/go/intro/v1 \
    proto/intro/v1/user.proto

Using the Generated Code

With code generated by protobuf-es, the schema becomes a native TypeScript API.

web/src/main.ts
import { create, toBinary, toJsonString } from "@bufbuild/protobuf";
import { UserSchema } from "./gen/intro/v1/user_pb";

const user = create(UserSchema, {
  id: 1042n,
  name: "cyber_ninja",
  age: 28,
  verified: true,
});

const bytes = toBinary(UserSchema, user);
const json = toJsonString(UserSchema, user);

console.log("JSON Output:", json);
console.log("Binary Output:", bytes);
Different languages and runtimes

The same schema-first workflow applies across supported languages, but import paths, package names, generated types, and runtime APIs differ by language. Getting-started tutorials for each language are listed under Further Reading.

Further Reading

Next

Services

Defining RPC interfaces with the Protobuf service keyword: unary and streaming methods, and how frameworks like gRPC and ConnectRPC generate clients and servers.