A simple package
This tutorial creates a napi-rs v3 addon, calls it from Node.js, and prepares the project for CI. Complete the prerequisites first.
Create the project
Choose a package name you control. A scope is strongly recommended because the release workflow creates one npm package per target.
The following command uses the Yarn template and supplies the non-interactive defaults for everything else:
npx @napi-rs/cli new cool \
--name @your-scope/cool \
--no-interactive
To use the pnpm template instead:
pnpm dlx @napi-rs/cli new cool \
--name @your-scope/cool \
--package-manager pnpm \
--no-interactive
Replace @your-scope before running the command. If you want to choose the
Node-API level, targets, license, and CI workflow interactively, omit
--no-interactive.
INFO
napi new supports the maintained Yarn and pnpm templates. It does not
generate a generic template that can be switched to an arbitrary package
manager afterward.
Understand the generated project
Enter the project and install its dependencies:
cd cool
yarn install
Use pnpm install for the pnpm template. The files you will work with first
are:
.
├── .github/workflows/CI.yml
├── Cargo.toml
├── build.rs
├── package.json
├── src/lib.rs
└── __test__/index.spec.ts
src/lib.rsis the Rust addon source.Cargo.tomldeclares acdyliband the napi-rs v3 crates.build.rscalls the napi-rs build setup and must remain at the crate root.package.jsoncontains the CLI scripts andnapipackaging config..github/workflows/CI.ymlbuilds and tests the target rows retained from the template.
npm/ is not present yet. The publish job creates its per-platform package
directories with napi create-npm-dirs after the platform builds finish.
The generated Rust source exports a small function:
#![deny(clippy::all)]
use napi_derive::napi;
#[napi]
pub fn plus_100(input: u32) -> u32 {
input + 100
}
The macro exposes the Rust function as the JavaScript function plus100 and
writes the corresponding TypeScript declaration during the build.
Build the addon
Run the template's release build for your current platform:
yarn build
pnpm build
The script invokes napi build --platform --release and produces files like:
cool.darwin-arm64.node
index.js
index.d.ts
The exact .node suffix follows your current OS, architecture, and ABI. A
Linux glibc build, for example, uses linux-x64-gnu. Use yarn build:debug
when you want a debug build.
The generated declaration contains:
export declare function plus100(input: number): number
Call the native function from Node.js:
node -e "const { plus100 } = require('./index.js'); console.log(plus100(42))"
The output is:
142
Change and test the Rust API
Add another exported function to src/lib.rs:
#[napi]
pub fn multiply(left: i32, right: i32) -> i32 {
left * right
}
Rebuild, then verify both the runtime export and generated types:
yarn build
node -e "const { multiply } = require('./index.js'); console.log(multiply(6, 7))"
Add a matching AVA assertion to __test__/index.spec.ts:
**test/index.spec.ts**
import test from 'ava'
import { multiply } from '../index'
test('multiply in native code', (t) => {
t.is(multiply(6, 7), 42)
})
Run the tests:
yarn test
Prepare the repository
Before pushing the generated workflow, update package.json:
- Set
nameto a package and scope you can publish. - Set
repositoryto the final GitHub repository. npm provenance checks this metadata, so do not leave the template repository URL in place. - Review
license,description,keywords,homepage, andbugs. - Review
napi.targets. It controls package creation and publishing, but each target still needs an actual CI build job.
If the name or binary name changes later, use the CLI so Cargo, package config, CI, and generated binding names stay aligned:
yarn napi rename \
--name @your-scope/cool \
--binary-name cool \
--repository https://github.com/your-name/cool.git
Then create and push the repository:
git init
git add .
git commit -m "Create napi-rs package"
git branch -M main
git remote add origin git@github.com:your-name/cool.git
git push -u origin main
Prepare npm and GitHub Actions
The generated workflow publishes through npm and creates a GitHub release. For its token-based setup:
- Create the npm scope and package access you intend to use.
- Create an npm automation token that can publish the root and every per-platform package.
- Add it as the
NPM_TOKENActions secret. - Keep the workflow's
contents: writepermission for GitHub releases andid-token: writepermission for npm provenance. - Run the ordinary CI path successfully before attempting a release.
WARNING
Publishing is not atomic. napi pre-publish updates package metadata,
publishes platform packages, and can create or update a GitHub release before
npm publishes the root package. Do not use it as a trial command with real
credentials.
Read Release native packages for the complete
preflight, release, and recovery procedure. The exact side effects and flags
are documented under napi pre-publish.
Where to go next
- Release native packages to publish your package to npm when you are ready.
- Testing and debugging to grow the test suite and debug the native side.
- Values for Rust-to-JavaScript conversions.
- Async functions for asynchronous exports.
- Support and compatibility before expanding the target matrix.
- Cross build for non-host builds.