On this page

One of the limitations of native addons is that they must be compiled for each target platform and architecture. Without pre-built binaries, every user who installs your package must have a working C/C++ toolchain on their machine.

node-pre-gyp solves this by letting you build binaries ahead of time, upload them to a remote location, and have users download the right binary at install time - falling back to compiling from source only if a matching binary is not available.

Note that Node-API support was added to node-pre-gyp in version 0.8.0.

prebuild is an alternative tool that addresses the same problem.

This page describes the changes required to a Node-API addon to support node-pre-gyp.

By default, node-pre-gyp uploads binaries to Amazon S3.

The node-pre-gyp-github module adds support for publishing to GitHub Releases instead.

Before uploading you need:

  1. An Amazon Web Services account.
  2. An IAM user or role with permission to upload to S3.
  3. An S3 bucket to host the binaries.

Never store credentials in your repository. node-pre-gyp supports two common approaches for providing credentials during development:

  1. A ~/.node_pre_gyprc file:

    {
      "accessKeyId": "xxx",
      "secretAccessKey": "xxx"
    }
  2. Environment variables:

    export node_pre_gyp_accessKeyId=xxx
    export node_pre_gyp_secretAccessKey=xxx

For CI environments, prefer IAM roles or short-lived credentials rather than long-lived access keys. See the node-pre-gyp credentials documentation for additional options.

The package is now published under the @mapbox scope. Use aws-sdk as a dev dependency for the upload step.

"dependencies": {
  "@mapbox/node-pre-gyp": "^1.0.0"
},
"devDependencies": {
  "aws-sdk": "^2.0.0"
}

The install script should invoke node-pre-gyp with --fallback-to-build so that users who don't have a pre-built binary available can still compile locally:

"scripts": {
  "install": "node-pre-gyp install --fallback-to-build"
}

The binary property tells node-pre-gyp which Node-API versions your addon supports and where to find/upload binaries:

"binary": {
  "module_name": "your_module",
  "module_path": "./lib/binding/napi-v{napi_build_version}",
  "remote_path": "./{module_name}/v{version}/{configuration}/",
  "package_name": "{platform}-{arch}-napi-v{napi_build_version}.tar.gz",
  "host": "https://your_bucket.s3.us-west-1.amazonaws.com",
  "napi_versions": [3]
}

Set module_name to a valid C identifier. The napi_versions array lists which Node-API versions to build for; 3 is a reasonable minimum for most addons.

See the node-pre-gyp docs for a complete reference, including Node-API considerations.

Add a post-build target to copy the compiled binary to the path specified by module_path:

{
  "target_name": "action_after_build",
  "type": "none",
  "dependencies": ["<(module_name)"],
  "copies": [
    {
      "files": ["<(PRODUCT_DIR)/<(module_name).node"],
      "destination": "<(module_path)"
    }
  ]
}

Include the Node-API version in the first target's defines so the header files configure themselves correctly:

"defines": [
  "NAPI_VERSION=<(napi_build_version)"
]

JavaScript code that loads the native binary must dynamically resolve the path to the correct .node file:

const binary = require('@mapbox/node-pre-gyp');
const path = require('path');
const bindingPath = binary.find(
  path.resolve(path.join(__dirname, './package.json'))
);
const binding = require(bindingPath);

Once everything is in place, build from source:

./node_modules/.bin/node-pre-gyp package
./node_modules/.bin/node-pre-gyp publish

Use GitHub Actions to build, test, and publish binaries for multiple platforms and architectures. A typical workflow matrix covers ubuntu-latest, macos-latest, and windows-latest, plus any architecture variants you need (e.g. x64, arm64). See the node-pre-gyp repository for example workflow configurations.