Skip to content

Getting started

JavaScript Protobuf should not force you to choose between correct Protobuf semantics and idiomatic TypeScript. Protobuf-ES gives you both: plain message objects, generated schema exports, ESM by default, typed fields, discriminated oneofs, binary and JSON serialization, reflection, extensions, and custom options.

This guide takes the fastest path from .proto to working code. You will generate TypeScript with the Buf CLI, create a typed message object, and serialize it with the same schema. Using the Buf CLI is the easiest way to run local code generation from npm, but Protobuf-ES is not tied to it: protoc-gen-es is a standard protoc plugin and fits existing Protobuf toolchains too.

Start with a new directory:

Terminal window
mkdir example
cd example
npm init -y
npm install typescript
npx tsc --init

Install the runtime library, the code generator, and the Buf CLI package:

Terminal window
npm install @bufbuild/protobuf
npm install --save-dev @bufbuild/buf @bufbuild/protoc-gen-es

Create proto/example.proto:

syntax = "proto3";
package example;
message User {
string first_name = 1;
string last_name = 2;
bool active = 3;
}

For this example, create buf.yaml:

version: v2
modules:
- path: proto

This declares proto as a Buf module, so generation can run from the project root.

Create buf.gen.yaml:

version: v2
plugins:
- local: protoc-gen-es
out: src/gen
opt: target=ts

Run the generator:

Terminal window
npx buf generate

target=ts generates TypeScript source. See Plugin options for JavaScript output, import extensions, JSON types, and other generator settings.

If you already use protoc, use the same plugin directly: mkdir -p src/gen && PATH=${PATH}:$(pwd)/node_modules/.bin protoc -I proto --es_out=src/gen --es_opt=target=ts example.proto.

Your project now looks like this:

.
├── buf.yaml
├── buf.gen.yaml
├── package.json
├── proto
│ └── example.proto
└── src
└── gen
└── example_pb.ts

Import the generated schema and the runtime helpers:

import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
import { type User, UserSchema } from "./gen/example_pb";
const user: User = create(UserSchema, {
firstName: "Homer",
lastName: "Simpson",
active: true,
});
const bytes = toBinary(UserSchema, user);
const roundTrip = fromBinary(UserSchema, bytes);
roundTrip.firstName; // "Homer"

create() constructs a message value. toBinary() serializes it. fromBinary() parses it back. Generated messages are ordinary objects, not class instances.