protobuf.js README

The project's README from github.com/protobufjs/protobuf.js. As one markdown file: README.md.

protobuf.js
protobuf.js

protobuf.js is a very fast, conformant, and unusually versatile JavaScript implementation of Protocol Buffers for Node.js and browsers. It is independently maintained with contributions from the upstream Protocol Buffers project, works with .proto schemas without requiring protoc, and supports runtime reflection as well as specialized code generation with matching TypeScript declarations.

If protobuf.js is important to your project or organization, or if you depend on it commercially, consider supporting its ongoing maintenance. Sponsorship helps make bug fixes, releases, LTS/security handling, and user support more sustainable.

Getting started

Getting up and running is simple: Install the package, load a schema, and you are all set to encode and decode Protobuf messages. From there, protobuf.js grows with your requirements: Add code generation with TypeScript declarations, transport-agnostic services, support for text-based formats, and more as needed.

Install

npm install protobufjs

The command line utility for generating reflection bundles, static code and TypeScript declarations is published as an add-on package:

npm install --save-dev protobufjs-cli

The CLI is a JS-native protobuf.js toolchain that does not require setting up protoc. If you prefer a protoc-based workflow, it provides protoc-gen-pbjs as an option.

Browser builds

Canonical browser builds are provided via the jsDelivr CDN, supporting CommonJS, AMD and global window.protobuf. Make sure to pin an exact version in production.

Usage

The examples below use this schema:

syntax = "proto3";

package awesomepackage;

message AwesomeMessage {
  string awesome_field = 1;
}

protobuf.js converts .proto field names to camelCase by default, so awesome_field is used as awesomeField in JavaScript. Use the keepCase option when loading or parsing .proto files to preserve field names as written.

Load a schema

const protobuf = require("protobufjs");

const root = await protobuf.load("awesome.proto");
const AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage");

Optionally use load() with a callback, or loadSync() for synchronous loading on Node.js. Imports resolve relative to the importing file by default. To resolve imports against a specific base directory, create a Root and override root.resolvePath before calling root.load().

Encode and decode

const payload = { awesomeField: "hello" };

// Optionally create a message instance from already valid data
const message = AwesomeMessage.create(payload);

const encoded = AwesomeMessage.encode(message).finish();
const decoded = AwesomeMessage.decode(encoded);

encode expects a message instance or equivalent plain object and does not verify input implicitly. Use create to create a message instance from already valid data when useful, verify for plain objects whose shape is not guaranteed, and fromObject when conversion from broader JavaScript objects is needed.

Plain objects can be encoded directly when they already use protobuf.js runtime types: numbers for 32-bit numeric fields, booleans for bool, strings for string, Uint8Array or Buffer for bytes, arrays for repeated fields, and plain objects for maps. Map keys are the string representation of the respective value or an 8-character hash string for 64-bit keys.

Note that, as with any structured binary format, decoded structures incur runtime memory overhead beyond their encoded representation. Applications processing untrusted input should therefore apply appropriate input-size and concurrency limits to bound memory use. For this reason, unknown fields present on the wire are intentionally discarded as the safer default, but can be retained by setting reader.discardUnknown = false per reader, or by setting Reader.discardUnknown = false to make it the default for subsequently created readers. Preserved unknown field data can also be explicitly dropped with delete message.$unknowns.

Convert plain objects

Conversion is an explicit interoperability boundary. fromObject accepts common JavaScript inputs such as enum values by name, base64 bytes, decimal 64-bit strings, Long, and BigInt; toObject lets callers choose the output expected by their application or transport.

const message = AwesomeMessage.fromObject({ awesomeField: 42 });
const object = AwesomeMessage.toObject(message, {
  longs: String,
  enums: String,
  bytes: String
});

Common ConversionOptions are:

Option Effect
longs: BigInt Converts 64-bit values to bigint values
longs: String Converts 64-bit values to decimal strings
longs: Number Converts 64-bit values to JS numbers (may lose precision)
enums: String Converts enum values to names
bytes: String Converts bytes to base64 strings
defaults: true Includes default values for unset fields
arrays: true Includes empty arrays for repeated fields
objects: true Includes empty objects for map fields
oneofs: true Includes virtual oneof discriminator properties

Message API

Message types expose focused methods for validation, conversion, and binary I/O.

Message instances provide stable runtime identity for testing with instanceof.

Length-delimited methods read and write a varint byte length before the message, which is useful for streams and framed protocols.

If required fields are missing while decoding proto2 data, decode throws protobuf.util.ProtocolError with the partially decoded message available as err.instance.

Code generation

Use protobufjs-cli to generate schema-specific code, either directly with pbjs or through the optional protoc-gen-pbjs plugin for protoc.

protobuf.js offers two code-generation modes, both with matching TypeScript declarations: reflection modules generate optimized code at runtime with reflection metadata retained; static modules generate reflection-free code ahead of time. Both use the same runtime package and automatically import only the runtime capabilities they need.

Target Output Runtime entry
json-module Reflection module protobufjs/light.js (without parser)
static-module Static code module protobufjs/minimal.js (without reflection)

Module targets support --wrap default for CommonJS and AMD, plus esm, commonjs, amd, and closure; --wrap can also load a custom wrapper module.

Static modules

Static modules emit dedicated, reflection-free JavaScript code for your schema.

npx pbjs -t static-module -w esm -o awesome.js --dts awesome.proto
import { awesomepackage } from "./awesome.js";

const message = awesomepackage.AwesomeMessage.create({ awesomeField: "hello" });

Static code is repetitive by design, but compresses unusually well with Brotli or gzip and works in CSP-restricted environments.

Reflection modules

Reflection modules wrap schemas as compact JSON metadata while avoiding .proto parsing at runtime.

npx pbjs -t json-module -w esm -o awesome.js --dts awesome.proto
import { awesomepackage } from "./awesome.js";

const AwesomeMessage = awesomepackage.AwesomeMessage;

Declarations for reflection modules mirror static-module typings. Because JSON modules export reflection objects, message instances should be created with MyMessage.create(...) rather than constructors. Code using create(...) works with static modules as well. Separately, -t json can serialize reflection metadata as bare JSON bundles for use with load() or Root.fromJSON().

TypeScript integration

protobuf.js works with TypeScript out of the box: its runtime API is typed, and generated code can be paired with matching TypeScript declarations in a single CLI invocation. Declarations are strongly typed, including discriminated unions for oneofs and scoped types for plain-object usage:

message Profile {
  oneof contact {
    string email = 1;
    string phone = 2;
  }
}
const profile = Profile.create({
  contact: "email",
  email: "[email protected]"
});

if (profile.contact === "email") {
  profile.email; // string
}

const decoded = Profile.decode(bytes);
if (decoded.contact === "phone") {
  decoded.phone; // string
}

The same narrowed shape is available for plain-object inputs:

const object: Profile.$Shape = {
  contact: "email",
  email: "[email protected]"
};

Advanced usage

Programmatic schemas

Schemas can be constructed directly through reflection:

const AwesomeMessage = new protobuf.Type("AwesomeMessage")
  .add(new protobuf.Field("awesomeField", 1, "string"));

const root = new protobuf.Root()
  .define("awesomepackage")
  .add(AwesomeMessage);

Custom message classes

A reflected type can use a custom class as its runtime constructor:

class AwesomeMessage extends protobuf.Message<AwesomeMessage> {
  awesomeField = "";

  constructor(properties?: protobuf.Properties<AwesomeMessage>) {
    super(properties);
    // ...
  }

  customInstanceMethod() {
    return this.awesomeField.toLowerCase();
  }
}

root.lookupType("awesomepackage.AwesomeMessage").ctor = AwesomeMessage;

const decoded = AwesomeMessage.decode(bytes);
decoded.customInstanceMethod(); // string

protobuf.js will populate the constructor with the usual static runtime methods and use it for decoded messages. When assigning constructors manually, add the type to its parent namespace/root first if fields reference other reflected types. In TypeScript, custom members are visible when using the custom class type in consuming code.

Services

protobuf.js supports service clients built from service definitions. The service API is transport-agnostic: provide an rpcImpl function to connect it to HTTP, WebSocket, gRPC, or another transport.

function myRpcImpl(method, requestData, callback) {
  // method.name
  // method.path
  // method.requestStream?
  // method.responseStream?
  performRequest(requestData, function(err, responseData) {
    callback(err, responseData);
  });
}

const myService = MyService.create(myRpcImpl/*, requestDelimited?, responseDelimited? */);

See examples/streaming-rpc.js for a streaming example.

See examples/grpc-service.js for an integration example with @grpc/grpc-js.

Extensions

The following extensions provide descriptor conversion and text-based protobuf formats when reflection metadata is available. Most applications only need the binary APIs above.

Descriptors

protobuf.js uses a compact JSON-based reflection representation internally. See ext/descriptor for use cases that need conversion between reflected roots and protoc descriptor messages.

ProtoJSON

Protocol Buffers support a special ProtoJSON format to share data with systems that do not support the binary wire format, for example when implementing gateways. Spec-compliant ProtoJSON is supported via ext/protojson.

Text Format

Protocol Buffers Text Format is a special syntax for representing protobuf data in text form, which can be useful for configurations or tests. Spec-compliant Text Format is supported via ext/textformat.

Conformance

protobuf.js is validated against the official Protocol Buffers conformance suite. It passes all required and recommended tests for the Proto2, Proto3 and Editions binary wire formats, with complete ProtoJSON and Text Format support available as optional extensions when those formats are needed.

Category Total Required Recommended
Binary 100.00% (2835/2835) 100.00% (1958/1958) 100.00% (877/877)
↳ Proto2 100.00% (707/707) 100.00% (489/489) 100.00% (218/218)
↳ Proto3 100.00% (707/707) 100.00% (486/486) 100.00% (221/221)
↳ Editions 100.00% (1421/1421) 100.00% (983/983) 100.00% (438/438)
ProtoJSON 100.00% (2814/2814) 100.00% (2362/2362) 100.00% (452/452)
TextFormat 100.00% (909/909) 100.00% (845/845) 100.00% (64/64)
Overall 100.00% (6558/6558) 100.00% (5165/5165) 100.00% (1393/1393)
Edition Supported since
2023 v7.5.0
2024 v8.0.0
2026 v8.8.0

Structured results of the conformance tests are also available as CI artifacts.

Performance

protobuf.js's reflection and static modes share the same code-generation backend and underlying hand-tuned reader and writer primitives. Reflection mode emits specialized encoders and decoders just in time, while static mode emits equivalent code ahead of time.

To see how this architecture compares in practice, the repository includes a reproducible benchmark suite measuring both modes alongside other general-purpose JavaScript implementations across three cases: our classic common message shape and two unmodified, structurally distinct fixtures sourced from other implementations. Each library is tested through its recommended serialization and deserialization path using identical schemas and message contents.

Encode Throughput
Show table
Implementation Common Vector tile Buf perf
protobuf.js static 5.28M ops/s   1.0x 3.06K ops/s   1.0x 49.2K ops/s   1.0x
protobuf.js reflect 5.16M ops/s   1.0x 3.08K ops/s   1.0x 47.5K ops/s   1.0x
JSON 3.44M ops/s   1.5x 1.50K ops/s   2.1x 7.13K ops/s   6.9x
protoc-gen-js 1.05M ops/s   5.0x 691 ops/s   4.5x 15.0K ops/s   3.3x
protoc-gen-es 1.22M ops/s   4.3x 1.14K ops/s   2.7x 35.8K ops/s   1.4x
Decode Throughput
Show table
Implementation Common Vector tile Buf perf
protobuf.js static 5.84M ops/s   1.1x 2.45K ops/s   1.1x 78.9K ops/s   1.0x
protobuf.js reflect 6.54M ops/s   1.0x 2.66K ops/s   1.0x 77.0K ops/s   1.0x
JSON 1.58M ops/s   4.1x 1.21K ops/s   2.2x 21.0K ops/s   3.8x
protoc-gen-js 701K ops/s   9.3x 946 ops/s   2.8x 21.5K ops/s   3.7x
protoc-gen-es 1.67M ops/s   3.9x 1.19K ops/s   2.2x 31.0K ops/s   2.5x

Structured results include environment details for this run and are committed alongside the charts. The suite also runs in CI.

To run the benchmark on your own hardware:

npm --prefix bench install
npm --prefix bench run generate
npm run bench

Compatibility

Supported runtimes are browsers, Node.js v12+, Deno and Bun. When using the CLI with Bun, Node.js must also be installed.

Security

Security-impacting reports are handled through coordinated GitHub Security Advisories where appropriate. See SECURITY.md for supported release lines and reporting instructions.

Development

git clone https://github.com/protobufjs/protobuf.js
cd protobuf.js
npm install
npm --prefix cli install

Running the tests:

npm test

Building the development and production versions with their respective source maps to dist/:

npm run build

Additional documentation

protobuf.js (protobufjs/protobuf.js)

Searching Database…

这是什么东西?protobuf.js 是一个速度极快、完全符合规范的 Protocol Buffers 的 JavaScript 实现,可用于 Node.js 和浏览器。它无需 protoc 就能直接使用 .proto 模式文件,既支持运行时反射,也支持提前生成带 TypeScript 声明的代码。
它是加到我项目里的库,还是命令行工具?它主要是一个 npm 库,用来在 JavaScript 和 TypeScript 中编码和解码 protobuf 消息。代码生成(反射打包、静态模块、.d.ts 声明文件)则由另一个附加的命令行包 protobufjs-cli 提供。
怎么把它装进我的项目并开始编码消息?运行 npm install protobufjs,加载你的 .proto 模式文件,然后就可以开始编码和解码消息了。在浏览器里,官方推荐的构建版本通过 jsDelivr CDN 提供——生产环境中请固定使用确切的版本号。
我需要先安装 protoc 或其他东西吗?不需要配置 protoc——这个命令行工具是纯 JS 的工具链(如果你习惯那种流程,它也可以作为 protoc-gen-pbjs 插件接入 protoc)。运行时需要 Node.js 12 或更高版本;它唯一的直接依赖是用于处理 64 位数值的 long 包。
解码不可信的数据时要注意什么?encode 不会隐式校验输入,所以对结构未知的普通对象要先调用 verify。解码后的消息比线上的二进制形式占用更多内存,因此要限制输入大小和并发量。未知字段默认会被丢弃,可以通过 reader.discardUnknown = false 保留下来。
它会在 JavaScript 里改动 .proto 的字段名吗?会的——字段名默认会转换成驼峰命名(camelCase),比如 awesome_field 会变成 awesomeField。如果想让名字保持原样,加载或解析 .proto 文件时传入 keepCase 选项即可。
它在 JavaScript 里怎么处理 64 位整数?通过转换选项来控制:longs 可以返回为 BigInt 值、十进制字符串或 JS 数字(后者可能丢失精度)。fromObject 也接受 Long、BigInt、base64 字节串、十进制 64 位字符串以及枚举名称作为输入。
它能和只认 JSON 的系统交换数据吗?可以——通过 ext/protojson 扩展提供完全符合规范的 ProtoJSON,适合网关对接那些不支持二进制线上格式的系统。Protocol Buffers 文本格式同样可用,在 ext/textformat 里,方便用于配置和测试。
相比基于 protoc 的生成器,为什么选 protobuf.js?按 README 的说法:它是纯 JS 工具链,无需配置 protoc;完全符合 Edition 2026 规范;基准测试成绩远超 protoc-gen-js 和 protoc-gen-es——并且在同一套运行时之上同时提供反射和静态代码生成两种模式。
Creator
protobufjs
Size
45.1 MB
Stars
10.6K
Age
13y
Commits · 90 Days