Skip to content

Using N API

Eugene Lazutkin edited this page Jun 11, 2026 · 2 revisions

N-API (also written as Node-API) is Node's stable C ABI for native addons. Unlike traditional V8/nan.js bindings, an N-API addon compiled against API level N loads on any runtime that supports level N or higher — without recompilation when Node bumps its major version.

For binary distribution this is a major matrix collapse. Where a V8-based addon has to ship platform x arch x Node-ABI binaries (typically 30–40 per release), an N-API addon ships platform x arch x N-API-level (typically 6–10).

Quick start

In your addon's package.json:

{
  "scripts": {
    "save-to-github": "save-to-github-cache --artifact build/Release/your-addon.node --napi 8",
    "install": "install-from-cache --artifact build/Release/your-addon.node --napi 8 || node-gyp rebuild"
  }
}

The asset filename for --napi 8 is:

${prefix}${platform}-${arch}-napi-v8${suffix}

For example: re2-linux-x64-napi-v8.node.br.

When --napi is not specified, the bin falls back to the legacy ABI slot (process.versions.modules) — exactly as before. The two modes are mutually exclusive per build; pick one or the other in your package.json scripts.

Choosing an N-API level

Pick the lowest N-API level that has all the APIs your code uses. The lower you target, the wider the runtime support — and since N-API is forward-compatible, a binary targeting level 8 keeps working on any newer Node, Bun, or Deno that supports level 8 or higher.

N-API level Minimum Node Notes
1 8.6 MVP. Effectively unused today.
4 10.16 Buffer, threadsafe-functions.
6 14.0 BigInt, dates, accessors.
8 16.0 Object freezing, instanceof improvements. Sensible floor for new projects.
9 18.17 Symbol APIs, finalizer ordering.
10 22.0 Async cleanup hooks, latest property descriptors.

(Always check Node's official version matrix — it's authoritative; this table is a quick reference.)

If you're not sure, 8 is a safe default. It's been around since Node 16, gets you everything most addons need, and Node 16 itself has been EOL since 2023, so anyone running supported Node already has it.

Runtime support

Runtime N-API support Notes
Node 18+ Full Recommended target; what this package's engines.node requires.
Bun 1.0+ Full Reads process.versions.napi; identical asset paths work.
Deno 2.0+ Best-effort Deno's N-API is reasonably stable but evolving. Identical asset paths should work; report issues if they don't.
Electron Use prebuildify This package doesn't yet emit Electron-specific filenames. File an issue if you need it.

The downloader itself doesn't gate on runtime — it sniffs process.platform / process.arch, downloads the matching napi-v${level} asset, and lets the addon's own load path do whatever runtime check it needs. If the running Node/Bun/Deno doesn't support the declared N-API level, the binary fails to load → verify-build fails → the install falls through to npm run rebuild (which will likely also fail with a useful compiler-side error). That's the intended behavior — the same fallback chain that's been in place since 1.0.

Configuration

Following the same convention as --host / --host-var / DOWNLOAD_HOST:

Direct Env var (default name) Env var (custom name)
install --napi 8 DOWNLOAD_NAPI=8 --napi-var MYPKG_NAPI then MYPKG_NAPI=8
save --napi 8 DOWNLOAD_NAPI=8 --napi-var MYPKG_NAPI then MYPKG_NAPI=8

Precedence (highest first):

  1. --napi <level> flag
  2. Env var named by --napi-var <NAME>
  3. DOWNLOAD_NAPI env var (default fallback)
  4. npm_config_platform_napi (advanced; see "Cross-builds" below)
  5. Nothing → fall back to legacy ABI slot

For libraries published to npm, prefer --napi-var YOURPKG_NAPI so consumers can override per-package without colliding with DOWNLOAD_NAPI for other addons in the same node_modules. This mirrors the existing --host-var / --skip-path-var / --skip-ver-var / --agent-var convention.

Cross-builds (advanced, discouraged)

install-from-cache sniffs platform/arch from the running runtime. If you're trying to populate a node_modules for a different target than the build machine (e.g., building a multi-arch Docker image with layers), the existing npm_config_platform / npm_config_platform_arch overrides apply — and npm_config_platform_napi is the N-API-mode equivalent.

In practice this is fragile (the addon's verify-build won't be able to test the foreign binary) and usually means you should be doing a real per-platform CI build instead. We document the override for symmetry; we don't recommend it.

CI: building the matrix

A typical N-API build matrix in .github/workflows/build.yml:

strategy:
  matrix:
    include:
      - {os: ubuntu-latest, target: linux-x64}
      - {os: ubuntu-latest, target: linux-arm64, cross: true}
      - {os: macos-latest, target: darwin-x64}
      - {os: macos-latest, target: darwin-arm64}
      - {os: windows-latest, target: win32-x64}

steps:
  - uses: actions/checkout@v6
  - uses: actions/setup-node@v6
    with:
      node-version: 24
  - run: npm ci
  - run: npm run rebuild        # builds your-addon.node for the runner's platform
  - run: npm run save-to-github # uploads with --napi 8 -> linux-x64-napi-v8.node.{br,gz}
    env:
      GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

For musl Linux (Alpine), add a separate job using a musl-based container:

- name: Build linux-musl
  runs-on: ubuntu-latest
  container: node:24-alpine
  steps: [...]

The bin auto-detects musl via getconf / ldd and tags the asset name accordingly (linux-musl-x64-napi-v8.node.br).

See also

Clone this wiki locally