PBF: Low-Level Protocol Buffers for JavaScript
PBF is a low-level, fast, and ultra-lightweight JavaScript library (3KB gzipped) for efficiently decoding and encoding Protocol Buffers (protobuf), a compact binary format for structured data serialization. Version 4.0.1 is the current stable release, which includes minor fixes building upon the significant 4.0.0 overhaul. The library is actively maintained, with a recent major release focusing on modernizing the codebase and developer experience. A key differentiator of PBF is its exceptional performance, consistently outperforming `JSON.parse`/`JSON.stringify` and other protobuf implementations like `protocol-buffers` in benchmarks. It supports lazy decoding and offers extensive customization options for reading and writing data, making it suitable for both Node.js and browser environments where performance and bundle size are critical considerations.
Common errors
-
ReferenceError: require is not defined
cause Attempting to use `require()` to import PBF or code generated by PBF in an environment configured for ES modules, or in PBF v4+ which is ESM-only.fixChange `const Pbf = require('pbf');` to `import Pbf from 'pbf';`. For generated code, change `const { readMyMessage } = require('./my-schema');` to `import { readMyMessage } from './my-schema.js';`. -
TypeError: pbf_1.default is not a constructor
cause This error often occurs when an ES module that uses a default export (like PBF v4) is incorrectly imported in a CommonJS context that doesn't properly handle `default` properties, or when a bundler/transpiler misinterprets the import.fixEnsure your project is configured for ES modules and use `import Pbf from 'pbf';`. If using TypeScript, check `tsconfig.json` for `"esModuleInterop": true` and `"module": "NodeNext"` or `"ES2020"`. -
TypeError: Cannot read properties of undefined (reading 'readMyMessage')
cause This typically indicates that generated read/write functions are being accessed incorrectly after the v4.0.0 code generation overhaul, which flattened exports from nested objects.fixReview your import statements for generated code. Instead of `import { read } from './my-schema.js'; read.MyMessage(pbf);`, use `import { readMyMessage } from './my-schema.js'; readMyMessage(pbf);`.
Warnings
- breaking PBF v4.0.0 completely dropped CommonJS support and is now exclusively distributed as an ES module. Direct `require()` statements will fail.
- breaking The underlying codebase was rewritten to use modern ES syntax in v4.0.0. While this improves maintainability and performance, it might break compatibility with very old JavaScript environments (e.g., Internet Explorer 11) that lack support for these features.
- breaking PBF's CLI tool for generating JavaScript modules from `.proto` files now outputs ES modules by default since v4.0.0. Previously generated CommonJS modules are incompatible with this new default.
- breaking Code generation in v4.0.0 was overhauled to produce simpler, more compact code with flat exports of read and write functions, rather than nested objects (e.g., `read.Example` vs `readExample`).
- gotcha The `Pbf` constructor's `buf` argument was incorrectly typed as mandatory in versions prior to 4.0.1, despite being optional in practice. This could lead to TypeScript errors when instantiating `new Pbf()` without arguments.
Install
-
npm install pbf -
yarn add pbf -
pnpm add pbf
Imports
- Pbf
const Pbf = require('pbf');import Pbf from 'pbf';
- { readMyMessage, writeMyMessage } (generated code)
const { readMyMessage, writeMyMessage } = require('./my-schema');import { readMyMessage, writeMyMessage } from './my-schema.js'; - compile
const { compile } = require('pbf/compile');import { compile } from 'pbf/compile';
Quickstart
import Pbf from 'pbf';
interface MyComplexData {
name: string;
version: number;
layer: {
name: string;
size: number;
};
}
function writeLayer(layer: MyComplexData['layer'], pbf: Pbf) {
pbf.writeStringField(1, layer.name);
pbf.writeVarintField(3, layer.size);
}
function writeData(data: MyComplexData, pbf: Pbf) {
pbf.writeStringField(1, data.name);
pbf.writeVarintField(2, data.version);
pbf.writeMessage(3, writeLayer, data.layer);
}
function readLayer(tag: number, layer: MyComplexData['layer'], pbf: Pbf) {
if (tag === 1) layer.name = pbf.readString();
else if (tag === 3) layer.size = pbf.readVarint();
}
function readData(tag: number, data: MyComplexData, pbf: Pbf) {
if (tag === 1) data.name = pbf.readString();
else if (tag === 2) data.version = pbf.readVarint();
else if (tag === 3) data.layer = pbf.readMessage(readLayer, {} as MyComplexData['layer']);
}
const myData: MyComplexData = {
name: 'TelemetryPacket',
version: 42,
layer: {
name: 'SensorReadings',
size: 1024
}
};
const pbfWriter = new Pbf();
writeData(myData, pbfWriter);
const encodedBuffer: Uint8Array = pbfWriter.finish();
console.log('Encoded buffer (Uint8Array):', encodedBuffer);
console.log('Encoded buffer length:', encodedBuffer.length, 'bytes');
const pbfReader = new Pbf(encodedBuffer);
const decodedData: MyComplexData = pbfReader.readFields(readData, {} as MyComplexData);
console.log('Decoded data:', decodedData);