Heximon Logo
Deploy

Hosts

Where a Heximon app's fetch server lives — the heximon() Vite dev server, standalone Node/Bun/Deno, Nitro, Nuxt, unplugin for other bundlers, library mode, and the mixed-runtime split.

Your Module, Controller, and provider classes are host-agnostic — the same source compiles and runs unchanged whether the Vite dev server, a standalone Node process, Nitro, or Nuxt serves it. What changes between hosts is only who owns the live fetch server and triggers the compile. You write your app once; you pick a host, and separately, a deploy target for where that host actually runs.

Pick a host

Each host is a Heximon package you add to a config file. It compiles your modules and mounts the generated routes onto its own server — you never write a bootstrap. Every host reads the same heximon.config.ts config file for compiler plugins, so switching hosts never touches your Heximon config.

HostPackageRole
Dev + standalone build@heximon/build/viteThe heximon() plugin — runs the compiler in memory and serves the app on pnpm dev, and on vp build emits a standalone dist/server.js for Node/Bun/Deno (no second config, no host-entry file).
Other bundlers@heximon/build/rollup, /rolldown, /esbuild, /webpack, /rspack are build tiers that compile your modules and emit a runnable { app, fetch } bundle — for folding Heximon into a project already built by one of those bundlers.
Standalone (Node / Bun / Deno)@heximon/http/serveThe universal runner vp build's entry calls — boots the built { fetch } bundle as a long-lived server, picking node:http / Bun.serve / Deno.serve automatically. No Nitro.
Node / Nitro / edge@heximon/nitroheximonNitroModule compiles your modules and mounts the route table inside Nitro — Node, Cloudflare Workers, Deno, Bun, Lambda from one codebase, picked by Nitro's preset.
Nuxt@heximon/nuxtServes a Heximon API alongside your Vue pages on Nuxt's own Nitro server, with SSR cookie propagation.
Library (publish a module)@heximon/libraryCompiles one Heximon module into a precompiled, installable package another app's imports array pulls in.

The same modules drive every host. A controller injects its dependencies from its constructor and declares routes by parameter type regardless of which host lands it.

Durable Objects, WebSockets, and Server-Sent Events (@heximon/durable, @heximon/websocket, @heximon/sse) are transports, not hosts — stateful capabilities that ride whichever host above is active. See Durable Objects, WebSockets, and Server-Sent Events.

Develop with the Vite plugin

In development the heximon() Vite plugin is the host. It runs the compiler over src/ on startup, holds the generated wiring as in-memory virtual modules, serves the app, and recompiles on every save — so pnpm dev is the only command you need while building. The plugin itself takes no arguments; your Heximon config — compiler plugins and deploy platform — lives in heximon.config.ts next to it.

vite.config.ts
import heximon from "@heximon/build/vite";
import { defineConfig } from "vite-plus";

export default defineConfig({
  plugins: [heximon()],
  server: { port: 3000 },
});
heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({ plugins: [new HttpPlugin()] });

You add one compiler plugin per Heximon feature you use — new CqrsPlugin(), new DomainEventsPlugin(), and so on. The plugin set is the only thing that grows as your app does; the host wiring stays the same.

Use another bundler with unplugin

Most apps develop on the Vite plugin and deploy with Nitro (below) — never hand-writing a bundler config. But if your project already builds with another bundler, @heximon/build carries the same compile step to Rollup, Rolldown, esbuild, webpack, and rspack through a per-bundler entry.

The @heximon/build/vite entry is the complete Vite plugin — the build-error page, the JSON preview, recompile-on-change, auto-reload, and WebSocket-upgrade handling in dev — because it rides the shared @heximon/dev core.

The /rollup, /rolldown, /esbuild, /webpack, and /rspack entries are build tiers: they compile your src/ to .heximon/ and let the bundler emit a runnable server bundle (diagnostics to the terminal — the live dev server is Vite-only). Every one of these factories takes no arguments — the same heximon.config.ts drives them all.

vite.config.ts
import heximon from "@heximon/build/vite";
import { defineConfig } from "vite-plus";

export default defineConfig({
  plugins: [heximon()],
});

Run standalone on Node, Bun, or Deno

When you want the built bundle to run as a plain long-lived server — no Nitro — vp build emits it from the same vite.config.ts that serves vp dev, with no second bundler config and no host-entry file:

terminal
vp build                 # → dist/server.js
node dist/server.js      # or: bun dist/server.js  /  deno run -A dist/server.js — same file, every runtime

Under the hood, @heximon/http/serve is the universal runner: vp build's synthesized entry hands the generated fetch to srvx, which picks the platform server (node:http / Bun.serve / Deno.serve) automatically. To control the listener (port/hostname) or wrap the app, write your own one-line host entry and bundle it with a build tier above (or point Vite's build.rollupOptions.input at it):

serve.ts
import { serve } from "@heximon/http/serve";
import { fetch } from "./.heximon/server.js";

await serve(fetch); // port defaults to PORT, then 3000

Node, Bun, and Deno run the full framework path — HTTP, DI, the ambient Context (AsyncLocalStorage), and CQRS/event dispatch — unchanged, and the generated entry drains in-flight requests on SIGTERM on each. Capabilities that need a specific platform (native addons like better-sqlite3, the cloudflare:workers Durable Object / D1 APIs) stay on Node or the Cloudflare runtime.

Deploy to Node, Workers, and the edge with Nitro

For production, Nitro is a host that covers almost every runtime from one codebase — there is no separate "plain Node" story; Nitro is the Node host, and every other runtime is just a preset of the same build: node-server for a long-running process, cloudflare_module for Workers, all from one codebase.

You list the bare module in nitro.config.ts — it accepts no inline Heximon config. Compiler plugins live in the same heximon.config.ts the Vite plugin reads. Nitro's build runs Heximon's compiler over your server directory and mounts the routes; there's no compile script to call. Nitro's own preset decides the deploy platform — heximon.config.ts's plugins are the only thing you declare there for Nitro.

nitro.config.ts
import { heximonNitroModule } from "@heximon/nitro/module";
import { defineNitroConfig } from "nitro/config";

export default defineNitroConfig({
  compatibilityDate: "2025-07-04",
  serverDir: "server",
  modules: [heximonNitroModule],
});
heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({ plugins: [new HttpPlugin()] });

Routes mount at their own contract paths — Controller<"/api"> owns /api. The host adds no prefix, so per-route rules, caching, and headers from Nitro apply to each route individually.

Serve a Heximon API inside Nuxt

When you want a Vue frontend and a Heximon API on the same server, Nuxt 5 is the host. Your Heximon modules live under Nuxt's server/ directory; your Vue pages live under srcDir.

The module forwards to the Nitro host underneath and accepts no inline Heximon config — the integration is just one string in modules, with compiler plugins declared in the same heximon.config.ts. Nuxt's own Nitro preset decides the deploy platform.

nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: "2025-07-04",
  srcDir: "app",
  modules: ["@heximon/nuxt"],
});
heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({ plugins: [new HttpPlugin()] });

Because the API and the frontend share one Nitro server, cookies set during SSR propagate to your Heximon routes without a network hop — login on a page, and a server-rendered fetch already carries the session.

Publish a module as a library

When a feature is meant to be reused across apps or teams, library mode compiles one Heximon module into a precompiled npm package. The consuming app lists it in imports, exactly like a local module, and links against a typed factory plus a manifest — never your source.

See Library mode for the requires/providers/exports boundary and the peerDependencies rule that keeps DI identity intact across the package boundary.

Split one deploy across two runtimes

On a hybrid deploy target (Vercel Functions, Netlify Functions) a single app can place some controllers on a fast edge function and others on a node function where the database lives — one build, two bundles, routed by prefix. See Mixed-runtime split.

Reach the edge with stateful transports

HTTP is the default transport, but stateful workloads need more. Controllers covers the HTTP dispatcher; Durable Objects adds Cloudflare's stateful actors, and WebSockets / Server-Sent Events add live connections.

These aren't separate hosts — they're plugins that ride whichever host is active, so the same Nitro deployment serves your HTTP routes and your ws:// channels together.

Every host shares one property: the compiled output has no eval and no JIT. That's the point — it runs on edge runtimes where reflection-based frameworks can't, because the wiring was decided at build time, not discovered at boot.

See also

Copyright © 2026