Skip to content

protobufjs/protobuf.js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,022 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

protobuf.js
protobuf.js

Protocol Buffers are a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more, originally designed at Google (see).

protobuf.js is a standalone JavaScript implementation of Protocol Buffers with TypeScript support for Node.js and the browser. It works with .proto files out of the box, is optimized for fast binary I/O, and supports runtime reflection as well as static code generation.

Getting started

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 small but capable standalone protobuf.js toolchain. It does not require protoc or a language plugin.

Choose a runtime

Import Includes Use when
protobufjs Reflection, Parser You load .proto files at runtime
protobufjs/light.js Reflection You load JSON bundles or build schemas programmatically
protobufjs/minimal.js Static runtime You only use generated static code

Reflection builds generate specialized code at runtime. The CLI can emit the same optimized code ahead of time for the minimal runtime. The full build includes the light build, and the light build includes the minimal runtime.

Browser builds

Pick the distribution matching your runtime variant and pin an exact version:

<!-- Full -->
<script src="https://cdn.jsdelivr.net/npm/protobufjs@8.X.X/dist/protobuf.min.js"></script>
<!-- Light -->
<script src="https://cdn.jsdelivr.net/npm/protobufjs@8.X.X/dist/light/protobuf.min.js"></script>
<!-- Minimal -->
<script src="https://cdn.jsdelivr.net/npm/protobufjs@8.X.X/dist/minimal/protobuf.min.js"></script>

Browser builds support CommonJS and AMD loaders and export globally as window.protobuf.

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 = protobuf.loadSync("awesome.proto");
const AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage");

Use protobuf.load() for the asynchronous variant.

Encode and decode

const payload = { awesomeField: "hello" };

// Optionally verify if the payload is of uncertain shape
const err = AwesomeMessage.verify(payload);
if (err) throw Error(err);

// 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 verify for plain objects whose shape is not guaranteed, create to create a message instance from already valid data when useful, and fromObject when conversion from broader JavaScript input 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/Long keys. Use fromObject when input may use broader JSON-style forms such as enum names, base64 strings for bytes, or decimal strings for 64-bit values.

Install long with protobuf.js when exact 64-bit integer support is required.

Convert plain objects

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

Common ConversionOptions are:

Option Effect
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.

  • verify(object: object): null | string
    Checks whether a plain object can be encoded as-is. Returns null if valid, otherwise an error message.

  • create(properties?: object): Message
    Creates a message instance from already valid data.

  • fromObject(object: object): Message
    Converts broader JavaScript input into a message instance.

  • toObject(message: Message, options?: ConversionOptions): object
    Converts a message instance to a plain object for JSON or interoperability.

  • encode(message: Message | object, writer?: Writer): Writer
    Encodes a message or equivalent plain object. Call .finish() on the returned writer to obtain a buffer.

  • encodeDelimited(message: Message | object, writer?: Writer): Writer
    Encodes a length-delimited message.

  • decode(reader: Reader | Uint8Array): Message
    Decodes a message from protobuf binary data.

  • decodeDelimited(reader: Reader | Uint8Array): Message
    Decodes a length-delimited message.

  • message#toJSON(): object
    Converts a message instance to JSON-compatible output using default conversion options.

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.

Schemas and code generation

Use protobufjs-cli to generate reflection bundles, static JavaScript code and TypeScript declarations.

Reflection keeps schemas as descriptors and generates optimized functions at runtime. Static code emits the same optimized functions ahead of time. The main tradeoffs are how schemas are loaded, how bundle size scales with schema size, whether runtime code generation is allowed by your environment, and whether reflection metadata should remain available at runtime.

Target Output Minimum Runtime
json JSON bundle protobufjs/light.js
json-module JSON bundle module protobufjs/light.js
static Static code custom wrapper/integration, not standalone
static-module Static code module protobufjs/minimal.js

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

Reflection bundles

Bundling schemas to JSON avoids reparsing .proto files and can reduce browser requests when schemas would otherwise be loaded separately.

npx pbjs -t json -o awesome.json awesome1.proto awesome2.proto ...
const bundle = require("./awesome.json");

const root = protobuf.Root.fromJSON(bundle);
const AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage");
npx pbjs -t json-module -w es6 -o awesome.json awesome.proto
const root = require("./awesome.js");

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

Reflection bundles can be loaded with protobufjs/light.js because the .proto parser is not required.

Static modules

npx pbjs -t static-module -w es6 -o awesome.js awesome.proto
npx pbts -o awesome.d.ts awesome.js
import { awesomepackage } from "./awesome.js";

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

Generated static code only needs protobufjs/minimal.js.

TypeScript integration

The protobuf.js runtime API is typed, but fields of dynamically loaded messages are only known at runtime. For statically typed messages and fields, either generate static code with declarations, or use reflection bundles with declarations generated from the equivalent static module:

npx pbjs -t json-module -w commonjs -o awesome.js awesome.proto
npx pbjs -t static-module awesome.proto | npx pbts -o awesome.d.ts -

Use create(...) instead of constructors with reflection-backed declarations.

Advanced usage

Programmatic schemas

The full and light builds can construct schemas 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

Message classes can be extended by reusing the generated constructor:

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

AwesomeMessage.customStaticMethod = function() {
  // ...
};
AwesomeMessage.prototype.customInstanceMethod = function() {
  // ...
};

Alternatively, a custom constructor can be registered:

function AwesomeMessage(properties) {
  // ...
}

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

Custom constructors are populated with static create, encode, encodeDelimited, decode, decodeDelimited, verify, fromObject, toObject, and the instance method toJSON. The reflected type is available as AwesomeMessage.$type and message.$type.

Services

protobuf.js supports service clients built from reflected service definitions. The service API is transport-agnostic: provide an rpcImpl function to connect it to HTTP, WebSocket, gRPC, or another transport. See examples/streaming-rpc.js for details.

Decorators

Experimental decorators are available for defining message classes directly in code.

  • Type.d(typeName?: string)   (optional)
    Annotates a class as a protobuf message type.

  • Field.d<T>(fieldId: number, fieldType: string | Constructor<T>, fieldRule?: "optional" | "required" | "repeated", defaultValue?: T)
    Annotates a property as a protobuf field.

  • MapField.d<T extends { [key: string]: any }>(fieldId: number, fieldKeyType: string, fieldValueType: string | Constructor<{}>)
    Annotates a property as a protobuf map field.

  • OneOf.d<T extends string>(...fieldNames: string[])
    Annotates a property as a protobuf oneof discriminator.

See examples/decorators.ts for an example.

Descriptors

For protobuf descriptor interoperability, see ext/descriptor. Note that because the internals of this package do not rely on google/protobuf/descriptor.proto, options are parsed and presented literally.

Content Security Policy

In CSP-restricted environments that disallow unsafe-eval, use generated static code instead of runtime code generation.

Conformance

protobuf.js targets full binary wire-format conformance for Proto2, Proto3 and Editions. CI runs the official Protocol Buffers conformance suite, with logs uploaded as artifacts.

Performance

In both reflection and static modes, protobuf.js builds specialized encoders and decoders for each message type instead of interpreting descriptors at runtime.

The repository includes a small benchmark for the bundled fixture in bench/. It compares protobuf.js reflection and static code against native JSON.stringify/JSON.parse and google-protobuf (protoc-gen-js). Results depend on hardware, Node.js version, and the message shape, so they should be treated as indicative rather than absolute.

One run on an AMD Ryzen 9 9950X3D with Node.js 24.13.0 and google-protobuf 4.0.2 produced:

benchmarking encoding performance ...

protobuf.js (reflect) x 2,492,315 ops/sec ±0.56% (94 runs sampled)
protobuf.js (static) x 2,316,421 ops/sec ±0.49% (96 runs sampled)
JSON (string) x 2,581,565 ops/sec ±0.22% (99 runs sampled)
JSON (buffer) x 2,086,216 ops/sec ±0.68% (92 runs sampled)
google-protobuf x 1,052,145 ops/sec ±0.24% (101 runs sampled)

          JSON (string) was fastest
  protobuf.js (reflect) was 3.8% ops/sec slower (factor 1.0)
   protobuf.js (static) was 10.5% ops/sec slower (factor 1.1)
          JSON (buffer) was 19.6% ops/sec slower (factor 1.2)
        google-protobuf was 59.3% ops/sec slower (factor 2.5)

benchmarking decoding performance ...

protobuf.js (reflect) x 6,053,370 ops/sec ±0.25% (98 runs sampled)
protobuf.js (static) x 6,081,190 ops/sec ±0.35% (97 runs sampled)
JSON (string) x 1,579,677 ops/sec ±0.11% (100 runs sampled)
JSON (buffer) x 1,383,484 ops/sec ±0.15% (98 runs sampled)
google-protobuf x 931,575 ops/sec ±0.25% (97 runs sampled)

   protobuf.js (static) was fastest
  protobuf.js (reflect) was 0.4% ops/sec slower (factor 1.0)
          JSON (string) was 74.0% ops/sec slower (factor 3.8)
          JSON (buffer) was 77.2% ops/sec slower (factor 4.4)
        google-protobuf was 84.7% ops/sec slower (factor 6.5)

benchmarking combined performance ...

protobuf.js (reflect) x 1,259,417 ops/sec ±0.33% (101 runs sampled)
protobuf.js (static) x 1,296,628 ops/sec ±0.20% (98 runs sampled)
JSON (string) x 848,422 ops/sec ±0.10% (100 runs sampled)
JSON (buffer) x 727,866 ops/sec ±0.30% (100 runs sampled)
google-protobuf x 477,041 ops/sec ±0.22% (101 runs sampled)

   protobuf.js (static) was fastest
  protobuf.js (reflect) was 3.0% ops/sec slower (factor 1.0)
          JSON (string) was 34.5% ops/sec slower (factor 1.5)
          JSON (buffer) was 43.9% ops/sec slower (factor 1.8)
        google-protobuf was 63.2% ops/sec slower (factor 2.7)

Run it locally with:

npm run bench

Development

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

Running the tests:

npm test

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

npm run build

Additional documentation

About

Protocol Buffers for JavaScript & TypeScript.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors