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.
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:
- An Amazon Web Services account.
- An IAM user or role with permission to upload to S3.
- 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:
-
A
~/.node_pre_gyprcfile:{ "accessKeyId": "xxx", "secretAccessKey": "xxx" } -
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/client-s3 as a dev dependency for the upload step.
"dependencies": { "@mapbox/node-pre-gyp": "^1.0.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.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:
npm install --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.