node-rs: native Rust crates shipped as Node.js packages
Node.js bindings ❤️ Rust crates
At a glance
- What is it?
- node-rs is a napi-rs monorepo that publishes Rust crates as Node.js packages, covering CRC32, xxhash, bcrypt, argon2, jsonwebtoken and a jieba Chinese segmenter, with prebuilt binaries and a WebAssembly fallback in the toolchain. It suits a Node service that hashes or verifies passwords in a hot path, and it is the wrong dependency for a script that runs once.
- Who is it for?
- node-rs fits a Node service where password hashing, checksum verification or token signing is on a hot path, because a native implementation removes the JavaScript overhead without changing the API shape, and the monorepo keeps the binding and the upstream crate version in one place.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- napi-rs/node-rs1,481 ★ · +5
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| napi-rs/node-rs | 1.5k | 1.5k |
5 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Six packages, each a binding to an existing Rust crate
The package table in the README is short and deliberate. @node-rs/crc32 is described as the fastest CRC32 implementation using SIMD, and it is built on crc32fast with the nightly feature enabled, with crc32c also in the dependency list. @node-rs/xxhash is a binding to xxhash-rust with the xxh32, const_xxh32, xxh64, const_xxh64, xxh3 and const_xxh3 features selected. @node-rs/bcrypt wraps bcrypt-rust. @node-rs/argon2 wraps argon2-rust with default features turned off. @node-rs/jsonwebtoken wraps the jsonwebtoken crate with the rust_crypto feature, which is notable because it means the implementation uses a pure Rust crypto backend rather than calling out to OpenSSL. The sixth, @node-rs/jieba, binds jieba-rs with the tfidf and textrank features for Chinese word segmentation. Five of the six are thin wrappers, which is the point.
napi-rs is the layer that makes this possible
The README says the packages make Rust crates binding to Node.js use napi-rs, and the Cargo.toml shows the machinery. The napi dependency is at version 3 with default features disabled and only the napi3 feature enabled, with napi-build at 2 and napi-derive at 3 with the type-def feature so that TypeScript definitions are generated rather than hand-written. That is the substantive difference from older addon toolchains, which compile a C++ addon and require a toolchain on every machine that installs the package. With the Node-API interface the binary is a stable ABI, so a prebuilt artefact works across Node versions without recompilation, and the JavaScript side falls back to a WebAssembly build when no native binary matches the platform.
The JavaScript side carries a WebAssembly fallback path
The dev dependencies show the shape of the fallback. The @napi-rs/wasm-runtime and @emnapi/runtime packages are present, along with emnapi, @emnapi/core and the @tybys/wasm-util helper, the emnapi packages at alpha versions. The napi-rs CLI is at 3.10.8. The pattern this produces is that a package installs a native binary for the current platform when one is available, and otherwise loads a WebAssembly implementation, which is slower but functional. For a consumer that matters in two directions. It makes the packages usable on platforms with no prebuilt binary at all, and it means the worst case is a slower path rather than a crash, which is a reasonable default for something that may be doing a checksum.
The workspace is a Cargo workspace and a Yarn workspace at once
The two manifests define parallel structures. The Cargo workspace lists seven members, a shared crates/alloc directory plus the argon2, bcrypt, crc32, jieba, jsonwebtoken and xxhash packages, with resolver 2. The root package.json is a private workspace with the packages/* glob, a lerna.json at the root, and a packageManager field pinning yarn at 4.18.1, with a .yarn/ directory and .yarnrc.yml for the Berry setup. The scripts show the division of labour: build runs the workspace build serially with a concurrency of 1, artifacts runs the per-package artifact step, build:ts compiles the TypeScript projects with tsc -b, and test runs ava. The serial build is the kind of detail that matters when a package produces a native binary, since parallel builds of several napi targets fight over the same target directory.
Release profiles are tuned for shipped binaries
The release profile in Cargo.toml is configured for artefacts rather than for a local build. codegen-units is set to 1 and lto is enabled, which trades compile time for runtime speed and smaller or better optimised code, both of which matter for a prebuilt binary shipped to thousands of machines. overflow-checks is turned off, which is a deliberate choice for a hot path but is a real semantic change, since arithmetic wraps instead of panicking. strip is set to symbols, so the shipped binary is smaller and does not leak symbol names, at the cost of a stack trace being less useful when something does go wrong. The workspace dependencies also include once_cell, rand, rand_core, zeroize, simdutf8, indexmap with serde, base64, mimalloc-safe and a global_alloc path dependency pointing at the local crates/alloc crate, which is the allocator the bindings use.
Testing, linting and formatting are wired through a monorepo script layer
The lint-staged configuration in the root package.json shows how a change to one file type is handled. JavaScript, TypeScript and TSX files are linted with oxlint --fix, files with a yaml, json, md or toml extension plus JavaScript ones are formatted with oxfmt --write, and Rust files are formatted with cargo fmt. The dev target installs and enables husky, and a postinstall script runs husky install, so the hooks are active on a fresh clone. The ava configuration points at the test globs under packages/*/__test__ and packages/bcrypt/__tests__, with the TypeScript extension registered, cache disabled, a one minute timeout, and node arguments that import the @oxc-node/core/register hook so that ava can run TypeScript sources directly. The test runner is therefore the TypeScript source, not a compiled build.
The binding layer is three crates and one feature flag
Looking at how a single package is put together clarifies the whole architecture. The workspace dependencies list the pieces every binding shares, and the napi entries are what define the JavaScript surface.
napi = { version = "3", default-features = false, features = ["napi3"] }
napi-build = "2"
napi-derive = { version = "3", default-features = false, features = ["type-def"] }Turning off default features and selecting napi3 explicitly keeps the generated binding on a single API level rather than whatever the defaults happen to be, and the type-def feature on napi-derive is what causes the TypeScript declarations to be emitted from the Rust definitions. That last point is the one that decides whether the package is pleasant to use from TypeScript, because a native module without generated types is a native module with an any in the middle of it.
MIT licensed, actively released, with a security policy
The licence is MIT, with a LICENSE file at the root, and the repository is not archived with the last push on 2026-10-15. The recent release list shows the packages moving together, with @node-rs/jieba at 2.0.4 and @node-rs/bcrypt at 2.0.0 both on 2026-10-06 and @node-rs/argon2 at 2.2.2 the same day, which is the pattern of a monorepo where a coordinated release touches every package. The root also carries a SECURITY.md for reporting vulnerabilities, a CODE_OF_CONDUCT.md, a renovate.json for dependency updates, a rust-toolchain.toml and rustfmt.toml, and a .taplo.toml for TOML formatting, plus a depracted/ directory holding packages that are no longer published. The homepage is node-rs.dev, which the README also points at as a playground.
Editorial conclusion
node-rs fits a Node service where password hashing, checksum verification or token signing is on a hot path, because a native implementation removes the JavaScript overhead without changing the API shape, and the monorepo keeps the binding and the upstream crate version in one place. It is a poor fit for a small script or a CLI that runs once, since loading a native addon costs more than the pure JavaScript alternative would, and a poor fit if you cannot ship prebuilt binaries for your target platforms. Verify first which platforms your deployment needs against the published binaries, and read the SECURITY.md in the repository root, since these packages handle credentials and the disclosure path matters.
Frequently asked questions
Which packages does node-rs publish?
Six: @node-rs/crc32 for a SIMD CRC32, @node-rs/xxhash binding xxhash-rust, @node-rs/bcrypt binding bcrypt-rust, @node-rs/argon2 binding argon2-rust, @node-rs/jsonwebtoken binding the jsonwebtoken crate with the rust_crypto feature, and @node-rs/jieba binding jieba-rs for Chinese word segmentation.
Do I need a Rust toolchain to install these packages?
The bindings are built with napi-rs version 3 and the Node-API interface, and the JavaScript side includes a WebAssembly fallback through the napi-rs wasm runtime and emnapi packages. The Rust toolchain is needed to build from source, not to consume a published binary.
What does the jsonwebtoken package use for cryptography?
The Cargo.toml selects jsonwebtoken version 11 with the rust_crypto feature enabled, so the implementation uses a pure Rust crypto backend rather than a native OpenSSL binding. This matters for deployment, since it removes the OpenSSL system dependency that a C-backed implementation would impose.
How are the packages built and tested?
The repository is both a Cargo workspace and a Yarn workspace. The root build script runs the per-package build serially with a concurrency of 1, the artifact script produces the per-package artefacts, and tests run with ava over the TypeScript sources in packages/*/__test__ and packages/bcrypt/__tests__, with a one minute timeout.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/napi-rs-node-rs)Star history chart for your README
[](https://hysenlabs.com/projects/napi-rs-node-rs)Was this useful?