Skip to content
Built with

Node.js, Bun and Deno ​

Ecosystem

Not every app has to run on Cloudflare. Set target in void.config.ts to build a server for Node.js, Bun or Deno. Run it on your own machine, in a container or with any host.

On this pageBuild your app for Node.js, Bun or Deno · Know what the build output contains · Check which features work and which behave differently off Cloudflare

json
{ "target": "node" }

Get started ​

1. Configure the target ​

json
// void.config.ts
{ "target": "node" }

2. Install the server dependency (Node.js only) ​

Node.js needs one extra dependency, @hono/node-server. Bun and Deno use their built-in HTTP servers.

Now you will install the dependencies.

bash
npm install @hono/node-server

Example output: npm finishes with added N packages and no errors.

3. Develop ​

Now you will start the dev server.

bash
npx vite dev

Example output: Vite prints a local URL such as http://localhost:5173/.

The dev server reloads routes and pages when you edit them.

4. Build and run ​

Now you will build the app and start the built server.

bash
npx vite build
node dist/ssr/index.js

Example output: The build finishes and writes files to dist/. The server starts and prints the address it listens on.

Now you will build and start the app with Bun.

bash
bunx vite build
bun dist/ssr/index.js

Example output: The build finishes and writes files to dist/. The server starts and prints the address it listens on.

Now you will build and start the app with Deno.

bash
deno run -A npm:vite build
deno run -A dist/ssr/index.js

Example output: The build finishes and writes files to dist/. The server starts and prints the address it listens on.

Run these commands from your app folder. The server listens on the PORT env variable, or on 3000 by default.

Deploy dist/ssr and dist/client together.

Build output ​

The build produces two entry points:

dist/
  ssr/
    app.js       ← Hono app with static asset middleware (default export)
    index.js     ← imports app.js and starts the HTTP server
  client/        ← static assets (pages mode only)
    .vite/manifest.json
    assets/
      ...
  • app.js: exports the Hono app. Use it to embed the app, test it or build a custom server.
  • index.js: imports app.js and starts the server. Run this file in production.

vite preview also uses app.js to serve your built app locally.

Installed packages that depend on void are bundled into dist/ssr. Some of their dependencies use a native addon or read files next to their own code. Add each such dependency to your app's dependencies, at the version the package uses. The build then leaves it to Node.

What works ​

These SDK features work the same on every target:

What is different ​

Environment variables ​

Set server values in your host's environment. Read them with process.env in Node.js or Bun, or Deno.env.get() in Deno. The void/env schema and secret workflow need Cloudflare.

For browser code, use Vite's import.meta.env.VITE_* values. They are embedded at build time. Never put secrets in them.

No Cloudflare bindings ​

These imports are not available on other targets. They cause a compile-time error:

ImportCF Feature
void/dbD1 (SQL database)
void/kvKV (key-value storage)
void/storageR2 (blob storage)
void/authSDK-managed Better Auth
void/aiWorkers AI
void/sandboxCloudflare Sandboxes
void/envTyped environment schema

For a database or storage, use an external provider and connect with standard Node.js libraries.

WebSocket route files (*.ws.ts) need Cloudflare. You can still use the void/ws browser client with a WebSocket server hosted elsewhere.

No cron job runtime ​

You can still define cron jobs in crons/. They compile into the bundle, but nothing runs them. On Cloudflare, Workers Cron Triggers call the scheduled handler. On Node.js, use an external scheduler to call the exported handler. Examples are node-cron, systemd timers or your host's cron.

No inbound email ​

Inbound email handlers and sendEmail need Cloudflare. Use an external mail provider on other targets.

No prerendering ​

Edge prerendering needs Cloudflare. Build-time static generation is available on all targets.

No void deploy ​

void deploy only handles Cloudflare and SDK platform deploys. For Node.js, Bun or Deno, run vite build. Then deploy dist/ with your host's workflow.

Ignored config fields ​

On other targets, the SDK warns about these Cloudflare-only void.config.ts fields and ignores them:

  • inference.bindings: Cloudflare binding configuration
  • remote: remote binding proxy
  • worker: Cloudflare-specific config
  • routing.revalidate: edge caching, only on Cloudflare

If you enable auth through void/auth, void/client or auth.ts, the build fails on other targets. Use Better Auth directly for Node.js, Bun and Deno.

Programmatic usage ​

app.js exports the Hono app. You can import it into a custom server:

ts
import app from './dist/ssr/app.js';

// Use with any Node.js HTTP framework
const response = await app.fetch(new Request('http://localhost/api/hello'));
console.log(await response.json()); // { message: "Hello from Node.js!" }

Use this for tests, for embedding in Express or Fastify, or for serverless hosts that accept a fetch handler.

Next steps ​

implemented.stream AI