OpenHarmony, Node.js, and WebAssembly addons
zig-napi
Build N-API modules with Zig for OpenHarmony, Node.js, and WASI, and generate the matching TypeScript declarations from the same exported surface.
- Zig 0.17.0+ minimum toolchain
- Node-API v4–v10 gated wrappers
- MIT licensed
00 · Build pipeline
One root, distinct build targets
Shared root
Zig export root
src/hello.zig
OpenHarmony
napi_build.nativeAddonBuildlibhello.so
Node.js
napi_build.nodeAddonBuildhello.<platform-arch-abi>.node
WASI
wasm32-wasi targethello.wasm32-wasip1.wasmUnshared · no workershello.wasm32-wasi.wasmShared · worker pool
Separate step
TypeScript declarations
napi_build.generateTypeDefinitionindex.d.ts
01 · Capability map
One addon surface, multiple outputs
Three runtime outputs
Use one Zig export surface to build OpenHarmony shared libraries, Node.js addons, and WASI modules in single-threaded or threaded flavors.
Typed JavaScript boundary
Generate declaration files from functions, classes, enums, async descriptors, structs, and unions.
N-API version gates
Select Node-API v4 through v10 behavior and fail early when wrappers need a newer runtime API.
Low-level escape hatches
Work directly with Object, String, Buffer, ArrayBuffer, TypedArray, DataView, External, and references.
02 · Setup
Install as a Zig package
The package exports both the napi module and the napi_build helpers used by examples in this repository.
zig fetchzig buildzig build -Dtarget=aarch64-linux-ohos
03 · Build recipes
Copy the shape that matches your target
ZON install
.{
.name = "appname",
.version = "0.0.0",
.minimum_zig_version = "0.17.0",
.dependencies = .{
.@"zig-napi" = .{
.url = "https://github.com/openharmony-zig/zig-napi/archive/refs/tags/<GIT_TAG>.tar.gz",
.hash = "HASH_GOES_HERE",
},
},
}
OpenHarmony
const std = @import("std");
const napi_build = @import("zig-napi").napi_build;
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const zig_napi = b.dependency("zig-napi", .{});
const napi = zig_napi.module("napi");
_ = try napi_build.nativeAddonBuild(b, .{
.name = "hello",
.napi_module = napi,
.root_module_options = .{
.root_source_file = b.path("./src/hello.zig"),
.target = target,
.optimize = optimize,
},
});
}
Node addon
const addon = try napi_build.nodeAddonBuild(b, .{
.name = "hello",
.napi_module = napi,
.node_api = .{
.version = .v8,
.experimental = false,
},
.root_module_options = .{
.root_source_file = b.path("./src/hello.zig"),
.target = target,
.optimize = optimize,
},
});
_ = addon;
Type definitions
const dts = try napi_build.generateTypeDefinition(b, .{
.root_source_file = b.path("./src/hello.zig"),
.output = b.path("index.d.ts"),
.napi_module = napi,
});
b.getInstallStep().dependOn(&dts.step);
04 · Type generation
TypeScript declarations stay close to Zig exports
Generated from Zig
Functions, classes, tuples, structs, enums, arrays, unions, async descriptors, and raw N-API wrappers are mapped into declaration files during the build.
Override when the public contract differs
napi.dts(value, "TypeScriptType")andnapi.Dts(T, "TypeScriptType")let exported declarations express a custom TypeScript-facing contract while keeping runtime values unchanged.
05 · Targets
Build surface
| Target | Detail |
|---|---|
| OpenHarmony | aarch64-linux-ohos, arm-linux-ohoseabi, x86_64-linux-ohos |
| Node.js | host target by default, with platform-specific .node output names |
| WASI | wasm32-wasip1 (single-threaded) and wasm32-wasip1-threads (shared memory) builds with generated wasi/wasip1 loaders |
| TypeScript | index.d.ts generation from the same addon root |
| Examples | basic, init, node, allocator, memory, and benchmark fixtures |
06 · Reference
API docs live on their own path
The standalone API reference covers module registration, build helpers, runtime values, binary wrappers, async descriptors, native ownership, errors, and custom TypeScript declaration overrides.
Use the API page when you need exact Zig signatures and wrapper behavior instead of the overview flow.