# Supporting Different JavaScript Runtimes (Node.js, Deno, Bun) with the Same API

> Learn how huggingface/transformers.js unifies the JavaScript API for Node.js, Deno, and Bun, enabling consistent filesystem access, caching, and model loading across environments.

- Repository: [Hugging Face/transformers.js](https://github.com/huggingface/transformers.js)
- Tags: internals
- Published: 2026-03-03

---

**`@huggingface/transformers` automatically detects whether it is running on Node.js, Deno, or Bun at import time and configures filesystem access, caching, and model loading accordingly, exposing a single unified API across all three runtimes.**

Transformers.js is engineered to execute **unmodified** on the three major JavaScript server runtimes while maintaining an identical public interface. Whether you `import { pipeline }` from a Node.js script, a Deno module, or a Bun file, the library's internal runtime detection layer in [`src/env.js`](https://github.com/huggingface/transformers.js/blob/main/src/env.js) abstracts away platform-specific globals like `process`, `Deno`, and `Bun` so that downstream code never needs conditional branching.

## Runtime Detection at Import Time

The foundation of universal compatibility lies in feature checks that execute immediately when the module loads. In [`packages/transformers/src/env.js`](https://github.com/huggingface/transformers.js/blob/main/packages/transformers/src/env.js), the library defines boolean flags by probing global objects:

```javascript
const IS_DENO_RUNTIME = typeof globalThis.Deno !== 'undefined';
const IS_BUN_RUNTIME  = typeof globalThis.Bun  !== 'undefined';
const IS_PROCESS_AVAILABLE = typeof process !== 'undefined';
const IS_NODE_ENV = IS_PROCESS_AVAILABLE && process?.release?.name === 'node' && !IS_DENO_WEB_RUNTIME;

```

These constants are exported through a frozen `apis` object, allowing internal modules to query the execution context without re-evaluating the checks:

```javascript
export const apis = Object.freeze({
    IS_DENO_RUNTIME,
    IS_BUN_RUNTIME,
    IS_NODE_ENV,
    // …other flags
});

```

The detection logic distinguishes **Node.js** by verifying `process.release.name`, **Deno** by checking `globalThis.Deno`, and **Bun** by testing `globalThis.Bun`. A special case, `IS_DENO_WEB_RUNTIME`, identifies when Deno operates in a browser-like environment where filesystem APIs are unavailable.

## Runtime-Aware Configuration Defaults

Rather than forcing developers to manually configure environment variables, the `env` singleton in [`src/env.js`](https://github.com/huggingface/transformers.js/blob/main/src/env.js) uses the detection flags to set sensible defaults for filesystem access, local model loading, and caching strategies:

- **`allowLocalModels`** – Enabled automatically when a filesystem is available (Node.js, Deno CLI, Bun), but disabled in browser or Deno Web environments.
- **`useFS`** – Mirrors filesystem availability across runtimes.
- **`useBrowserCache`** – Activated only when the Web Cache API is present (Deno Web, browsers).
- **`useWasmCache`** – Enabled whenever either filesystem or Cache API storage is available.

The default cache directory (`DEFAULT_CACHE_DIR`) resolves to a valid path only when `IS_FS_AVAILABLE` is true, remaining `null` in ephemeral environments. This conditional setup ensures that **Node.js** and **Bun** default to disk-based caching, while **Deno** running with Web APIs utilizes the Cache API instead.

## Unified Public API Design

All runtime-specific complexity is hidden behind the `env` configuration object. End users interact with a plain JavaScript object regardless of the underlying platform:

```javascript
import { env, pipeline } from '@huggingface/transformers';

// Disable remote model fetching – works identically on all three runtimes
env.allowRemoteModels = false;

// Set a custom cache directory – effective only where filesystem access exists
env.cacheDir = '/tmp/transformers-cache';

```

Internally, modules such as [`src/utils/model-loader.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/model-loader.js) consult `env` and `apis` to decide whether to invoke `fs.readFile`, `fetch`, or the **Cache API** when retrieving model weights. This abstraction guarantees a single source of truth for configuration while permitting runtime-specific optimizations.

## Cross-Runtime Module Resolution

Handling ESM and CommonJS differences across runtimes requires a safe wrapper that resolves the library's own directory without breaking on bundlers. The code in [`src/env.js`](https://github.com/huggingface/transformers.js/blob/main/src/env.js) uses a defensive pattern:

```javascript
let dirname__ = './';
if (RUNNING_LOCALLY) {
    const _import_meta_url = Object(import.meta).url;
    if (_import_meta_url) {
        dirname__ = path.dirname(path.dirname(url.fileURLToPath(_import_meta_url))); // ESM
    } else if (typeof __dirname !== 'undefined') {
        dirname__ = path.dirname(__dirname); // CommonJS fallback
    }
}

```

The `Object(import.meta).url` syntax prevents Webpack and similar bundlers from inlining `import.meta` in CommonJS builds, while the `__dirname` fallback supports Node.js CommonJS modules and Bun's dual-mode support. Since Deno exclusively supports ESM, it follows the first branch automatically.

## Runtime-Agnostic WASM Caching

The ONNX Runtime backend preloads WebAssembly binaries through a unified caching layer defined in [`src/backends/utils/cacheWasm.js`](https://github.com/huggingface/transformers.js/blob/main/src/backends/utils/cacheWasm.js). The system consults `env.useWasmCache`, `env.useBrowserCache`, and `env.useFSCache` to determine storage strategy:

- **Node.js and Bun** cache WASM binaries to the local filesystem.
- **Deno Web** and browsers cache via the Cache API.
- **Deno CLI** uses filesystem storage like Node.js.

All implementations share the same cache key (`env.cacheKey`), ensuring that the precompiled binary is reused efficiently regardless of whether the code executes in a server or browser-like context.

## Code Examples

### Node.js Example

```javascript
// node-example.mjs
import { env, pipeline } from '@huggingface/transformers';

// Node.js has filesystem access, so local caching works by default
env.cacheDir = './.cache';

const generator = await pipeline('text-generation', 'gpt2');
const output = await generator('Hello world');
console.log(output);

```

### Deno Example

```bash

# Run with necessary permissions

deno run --allow-net --allow-read --allow-write deno-example.mjs

```

```javascript
// deno-example.mjs
import { env, pipeline } from 'npm:@huggingface/transformers';

// Explicitly allow local models (Deno CLI has FS access)
env.allowLocalModels = true;
env.cacheDir = '/tmp/transformers-cache';

const generator = await pipeline('text-generation', 'gpt2');
console.log(await generator('Deno rocks!'));

```

### Bun Example

```javascript
// bun-example.mjs
import { env, pipeline } from '@huggingface/transformers';

// Force offline mode and use local model weights
env.allowRemoteModels = false;
env.localModelPath = './models/';

const summarizer = await pipeline('summarization', 'sshleifer/distilbart-cnn-12-6');
console.log(await summarizer('Bun is fast and modern!'));

```

## Summary

- **Automatic detection** in [`src/env.js`](https://github.com/huggingface/transformers.js/blob/main/src/env.js) identifies Node.js, Deno, and Bun by checking `process.release.name`, `globalThis.Deno`, and `globalThis.Bun`.
- **Conditional defaults** for `allowLocalModels`, `useFS`, and caching methods eliminate manual configuration for each runtime.
- **Unified `env` API** exposes a single configuration interface that works identically across all platforms.
- **Safe module resolution** handles both ESM (`import.meta.url`) and CommonJS (`__dirname`) without bundler issues.
- **Runtime-agnostic WASM caching** stores ONNX binaries via filesystem or Cache API depending on environment capabilities.

## Frequently Asked Questions

### How does Transformers.js detect which JavaScript runtime is executing the code?

The library performs feature checks at import time in [`packages/transformers/src/env.js`](https://github.com/huggingface/transformers.js/blob/main/packages/transformers/src/env.js) by testing for runtime-specific globals: `globalThis.Deno` for Deno, `globalThis.Bun` for Bun, and `process.release.name === 'node'` for Node.js. These results are stored in the read-only `apis` object for internal reference.

### Can I use the same import statement for Node.js, Deno, and Bun?

Yes. While Deno requires the `npm:` specifier (e.g., `import from 'npm:@huggingface/transformers'`), the public API surface—including the `env` configuration object and `pipeline` function—remains identical. All three runtimes support the same method signatures and configuration options.

### Does local model loading work on all three runtimes?

Local model loading is automatically enabled for Node.js, Bun, and Deno CLI environments where filesystem access is available. However, when Deno runs in a Web-compatible mode without filesystem permissions, or in browser environments, `env.allowLocalModels` defaults to `false` to prevent errors.

### How is caching handled differently across Node.js, Deno, and Bun?

The library adapts its caching strategy based on runtime capabilities. Node.js and Bun cache WASM binaries and model files to disk using the local filesystem. Deno uses the filesystem when running as a CLI tool, but switches to the Web Cache API when operating in browser-like environments where `globalThis.caches` is available.