# js-bson: the BSON parser behind every Node MongoDB document

> bson is the package that encodes JavaScript objects into the binary form MongoDB stores and decodes them back, and it is maintained as part of the MongoDB Node.js driver organisation. It is Apache-2.0 licensed, ships type definitions, works in a browser without a bundler, and publishes signed releases with npm provenance attestations.

**mongodb/js-bson** — BSON Parser for node and browser

- Repository: https://github.com/mongodb/js-bson
- Stars: 1,216 · Forks: 274
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/mongodb-js-bson

## The wire format, separated from the driver

BSON is short for Binary JSON, and it is the binary-encoded serialization of JSON-like documents, with the format specified at bsonspec.org. The reason the format exists rather than plain JSON is that MongoDB needed a document encoding that is fast to parse, self-describing about its types, and able to carry values JSON cannot, which is why the library exposes things like ObjectId as first class objects rather than strings. What the js-bson repository is, then, is the codec for that format, and it is deliberately separate from the driver. The package description is a bson parser for node.js and the browser, the author is The MongoDB NodeJS Team, and the repository is mongodb/js-bson. Keeping the codec separate from the connection layer means it can be used on its own, which is what makes it useful for tooling, for a queue, or for a service that receives a BSON payload over a raw socket. It is written in TypeScript, published under the Apache-2.0 licence, and the package version in the manifest is 7.3.3.

## Serializing a document, and the compatibility table that constrains you

Installation is a single command:

```sh
npm install bson
```

and the first use is a serialize, a deserialize and a print. Under a bundler or in Node you import by package name, with an ES module import or the CommonJS require alternative:

```js
import { BSON, EJSON, ObjectId } from 'bson';
// or:
// const { BSON, EJSON, ObjectId } = require('bson');

const bytes = BSON.serialize({ _id: new ObjectId() });
console.log(bytes);
const doc = BSON.deserialize(bytes);
console.log(EJSON.stringify(doc));
// {"_id":{"$oid":"..."}}
```

The ObjectId is worth pausing on, because it is the clearest illustration of why BSON is not JSON with a different spelling. An ObjectId is a twelve byte value with a timestamp, a machine identifier and a counter in it, and the EJSON output shows it as a wrapper object with a $oid key. That is the format that keeps the value lossless through a JSON round trip, which is exactly what you want when a document has to survive a trip through an HTTP API, a log line or a message queue. The other thing to notice in that example is that the two steps are separate, serialize to bytes and deserialize back, and that the human-readable form is produced by EJSON rather than by JSON.stringify. The constraint that will bite you first is the compatibility table. The README states that only the following version combinations with the MongoDB Node.js Driver are considered stable, and the table shows a diagonal: bson@7.x with mongodb@7.x, bson@6.x with mongodb@6.x, bson@5.x with mongodb@5.x, bson@4.x with mongodb@4.x, and bson@1.x with mongodb@3.x. Everything else in the grid is N/A.

## EJSON and the operator injection warning in the docs

EJSON is the extended JSON layer, and the documentation gives it four entry points: parse, stringify, serialize and deserialize, each with optional settings. The parse function has a relaxed option that defaults to true, which attempts to return native JavaScript types where possible rather than BSON types. The example makes the difference concrete, parsing a document that contains an int32 typed value and printing two different results. With relaxed set to false the value comes back as a BSON Int32 object, and with the default relaxed behaviour it comes back as a plain number:

```js
const { EJSON } = require('bson');
const text = '{ "int32": { "$numberInt": "10" } }';

console.log(EJSON.parse(text, { relaxed: false }));
console.log(EJSON.parse(text));
```

Under the parse documentation there is a warning that deserves more attention than most people give it. If you use the result of EJSON.parse in a query, update or command, an attacker can inject operators or unexpected field values that change the meaning of the operation, and the risk is greatest when the input text comes from a source your application does not control. The recommendation is to validate untrusted input before parsing it, with a link to the client libraries best practices page in the MongoDB documentation. That is a genuinely important note, because EJSON is the format people reach for when they want to accept a document over HTTP, and a parsed document containing a $ne or $where key is a query, not just data. The relaxed flag also matters for correctness, because a number that is relaxed into a JavaScript number loses the distinction between an int32, a double and a long, and the difference can matter to a schema check downstream.

## A browser path that needs no bundler

The library has a browser story that does not assume a build step. If you are working directly in the browser without a bundler, the documentation tells you to use the .mjs bundle, and the example is a module script tag that imports the named exports from the local file:

```html
<script type="module">
  import { BSON, EJSON, ObjectId } from './lib/bson.mjs';

  const bytes = BSON.serialize({ _id: new ObjectId() });
  console.log(bytes);
</script>
```

The reason this is worth a separate section is that the package ships both a source form and a built form, and the files array in the manifest publishes lib, src, bson.d.ts, etc/prepare.js and vendor. The published type declarations are bson.d.ts, and the manifest also points at a types entry, so TypeScript consumers get types from the same package. The build itself is Rollup, with the rollup plugin for TypeScript and for node resolution, and the documentation is generated with typedoc and tsdoc, with the api-extractor configuration in the tree for the public API surface. For a browser application that only needs to read or write BSON, this is a reasonable footprint, and the fact that the same library runs in Node and in the browser means the encoding on one side and the decoding on the other cannot drift.

## Signed releases, npm provenance, and the JIRA workflow

The release integrity section is more careful than most library documentation. Releases are created automatically and signed using the Node team's GPG key, and all release packages provided as part of a GitHub release are signed. To verify, you import the key and check the detached signature file that sits alongside the tarball:

```shell
gpg --import node-driver.asc
npm view bson@vX.Y.Z dist.tarball
gpg --verify bson-X.Y.Z.tgz.sig bson-X.Y.Z.tgz
```

There is an honest caveat attached to that workflow. No GPG verification is done when you install the package with npm, and the contents of the GitHub tarball and npm's tarball are identical. That is why the second mechanism exists: releases published to the npm registry also include a provenance attestation that cryptographically links the package to its source repository and build workflow, and you check it with `npm audit signatures`. Between the two, a consumer can establish that the bytes on disk came from the repository the project publishes, which for a package that sits in the dependency path of most Node MongoDB deployments is a reasonable thing to want. The bug and feature workflow follows the MongoDB pattern, with issues filed in JIRA at jira.mongodb.org under the NODE project, and the README notes that bug reports in JIRA for the NODE driver project are public.

## The repository, and how it is put together

The tree shows a package built with the tooling a large organisation uses rather than a hobby project. There is src/ for the TypeScript sources, test/ with Mocha, chai, sinon and sinon-chai, an .nycrc.json and the istanbul coverage config for nyc, a benchmark/ directory, a rollup.config.mjs, api-extractor.json for the public API report, typedoc.json and tsdoc.json for the documentation, a .prettierrc.json and an .eslintrc.json, and a .githooks/ directory. The release process is automated with release-please, and the tree has both .release-please-manifest.json and release-please-config.json, a HISTORY.md for the human-readable changelog, and an sbom.json for the software bill of materials. Evergreen is the continuous integration directory. There is also a newer layer: an .agents/ directory, a .claude/ directory, an AGENTS.md and a CLAUDE.md, and the README explains that the repository uses agentskills.io conventions for AI coding agent instructions, with AGENTS.md as the canonical source of truth and tool-specific files like CLAUDE.md generated from it. The procedure for a nested directory is spelled out too, which is a sign of how widely that convention has spread: create an AGENTS.md in the target directory, add it, run scripts/symlink-claude-md.sh to generate the companion CLAUDE.md, and note that the script is vendored from an internal repository and should not be edited directly. The recent releases are v7.3.3 from 2026-09-15, v7.3.2 from 2026-08-11 and v7.3.1 from 2026-06-24, and the last push was on 2026-10-14.

## Where bson is the wrong tool

Three limits are worth stating. The first is that BSON is a document format for a database, not a general interchange format, and using it as one inherits MongoDB semantics rather than a neutral encoding. A document with a $ prefixed key is not merely a string with a dollar sign, and the EJSON warning about operator injection is a symptom of that. If you need a wire format between services that is language neutral and self-describing without query semantics, JSON is the safer choice, and BSON buys you speed and type fidelity at the cost of that neutrality. The second is the compatibility discipline. The stable combination table is a diagonal, so when the driver moves a major version you move with it, and pinning an old bson to keep a feature working means running an unsupported combination. The third is the scope of what the library does. It encodes and decodes; it does not connect, it does not index, it does not query, and it is not a schema validator. A team that reaches for it because they want MongoDB behaviour in a Node service should use the driver, and a team that wants to read and write BSON outside a driver connection, to cache a document or to move one between processes, gets exactly what it needs here. And the release process, with GPG signatures, provenance attestations and a public JIRA, is what a package at the centre of a dependency path should look like, and is worth a look even if you decide to use a different library.

## Conclusion

Adopt bson if you need to encode or decode BSON outside a MongoDB connection, for example to cache a document, move one between processes, or read a raw dump, and you want the same implementation the driver itself uses. Do not adopt it as a general purpose serialisation library, since BSON is a document format with MongoDB semantics rather than a schema-free interchange format, and treat EJSON.parse on untrusted input with care, because the documentation warns that its result can inject operators into a query. Before you upgrade, check the compatibility table, since only one pairing of bson major and driver major is marked stable at a time, and the current release is v7.3.3 from 2026-09-15.

## FAQ

### What is the bson package?

It is a BSON parser for Node.js and the browser, maintained by the MongoDB NodeJS Team and licensed under Apache-2.0. BSON is the binary-encoded serialization of JSON-like documents that MongoDB stores, and the package provides serialize, deserialize and the EJSON helpers.

### Which version of bson works with which MongoDB driver?

Only one pairing of major versions is considered stable at a time: bson@7.x with mongodb@7.x, bson@6.x with mongodb@6.x, bson@5.x with mongodb@5.x, bson@4.x with mongodb@4.x, and bson@1.x with mongodb@3.x. Every other combination in the table is marked N/A.

### Is it safe to parse EJSON from an untrusted source?

Not without validation. The documentation warns that if you use the result of EJSON.parse in a query, update or command, an attacker can inject operators or unexpected field values that change the meaning of the operation, and that the risk is greatest for input your application does not control.

### How do I verify that a bson release is authentic?

Import the Node team's key with `gpg --import node-driver.asc`, get the tarball URL with `npm view bson@vX.Y.Z dist.tarball`, then check the detached signature with `gpg --verify bson-X.Y.Z.tgz.sig bson-X.Y.Z.tgz`. npm registry releases also carry a provenance attestation, checked with `npm audit signatures`.

### Can I use bson in a browser without a bundler?

Yes. The documentation shows a module script tag that imports the named exports from the local .mjs bundle, for example `import { BSON, EJSON, ObjectId } from './lib/bson.mjs';`, and the same call then serializes and deserializes a document.

## Sources

- [Issues](https://github.com/mongodb/js-bson/issues)
- [License: Apache-2.0](https://github.com/mongodb/js-bson/blob/main/LICENSE)
- [mongodb/js-bson on GitHub](https://github.com/mongodb/js-bson)
- [README](https://github.com/mongodb/js-bson/blob/main/README.md)
- [Releases](https://github.com/mongodb/js-bson/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mongodb-js-bson
