UnFFI
Runtimes

Node.js (native)

Using unffi with native node:ffi (Node ≥26.3.0) — no build step, built-in FFI.

Node ≥26.3.0 ships node:ffi as a stable built-in module. unffi detects it automatically — no schema changes needed when upgrading from koffi.

# Node ≥26.3.0 — stable, no flag needed
node --experimental-ffi main.mjs

Native node:ffi is stable in Node 26.3.0+. Earlier 26.x versions require --experimental-ffi and may have incomplete ABI support — unffi falls back to koffi in that case.

Quick start

import { ,  } from 'unffi'

const  = await ('./libmath.dylib', {
  :  { : [., .], : . },
  : { : [.],        : . },
})

const sum  = ..(3, 4)
const sum: number
const root = ..(2.0)
const root: number

Feature parity

All core unffi features work identically with the native backend:

  • Primitives: t.i8t.i64, t.u8t.u64, t.f32, t.f64
  • Bool: t.bool (returned as boolean)
  • CString: t.cstring (auto-encodes input string, decodes output)
  • Buffer: t.buffer for zero-copy TypedArray arguments
  • Callbacks: t.fn(args, returns) for function pointers
  • Async: async: true for non-blocking FFI

Type compatibility

node:ffi always returns bigint for i64/u64. koffi returns number for small values — if you're migrating, use Number() for cross-backend compatibility:

const val = Number(lib.symbols.add_i64(1n, 2n))  // 3

t namespace

The t object on Node exports the same KoffiT interface for both backends, so your schema works unchanged whether native node:ffi or koffi is active.

Koffi features

Some koffi-specific features (t.koffi.struct, t.koffi.out, opaque handles, wide strings) are only available via the koffi adapter. See the koffi runtime docs for details.

On this page