Deployment
app.fetch(request) is a pure Web-standard handler, so the same app runs anywhere. Pick a runtime; the code doesn't change.
One command per target
create-nifra's site template ships every target's build + server entry and config. Pass --deploy <target> to make one the default — it points build/deploy at that target and fills in your project name. The per-target build:*/deploy:* scripts stay, so you can switch any time.
# Scaffold the multi-target site with a chosen default deploy target:
bun create nifra my-app --deploy vercel # or: bun | node | deno | cf-pages
# add --framework to pick the UI too (react default):
bun create nifra my-app --framework svelte --deploy vercel
cd my-app && bun install
bun run dev # local preview
bun run build # builds for the chosen target
bun run deploy # runs that target's deploy CLI (you stay logged-in to the vendor)
# Or add --ci github to also emit a deploy-on-push GitHub Actions workflow:
bun create nifra my-app --deploy cf-pages --ci github
# → .github/workflows/deploy.yml (builds on every push/PR, deploys on push to main)| target | build → | deploy | config scaffolded |
|---|---|---|---|
| Bun (flagship) | dist-bun/ | bun run start (any host) | — |
| Node | dist-node/ | docker build … && docker run | Dockerfile + .dockerignore |
| Deno Deploy | dist-deno/ | deployctl deploy | deno.json |
| Cloudflare Pages | dist/ | wrangler pages deploy | wrangler.toml |
| Vercel Edge | .vercel/output/ | vercel deploy --prebuilt | Build Output API v3 |
Nifra never runs the deploy or enters your cloud credentials — it scaffolds the config + a deploy script that shells out to the vendor CLI you've already authed.
CI: deploy on push
Add --ci github to emit a .github/workflows/deploy.yml tuned to the chosen target — it builds on every push/PR and deploys on a push to main. cf-pages uses cloudflare/wrangler-action, vercel the prebuilt vercel deploy, deno deployctl (OIDC). The workflow's header comment lists the exact repo secrets to set (e.g. CLOUDFLARE_API_TOKEN, VERCEL_TOKEN). Self-hosted bun/node have no universal push-to-deploy, so their workflow builds + uploads the bundle as an artifact and leaves a clearly-marked, host-specific deploy step for you to fill in (Fly, a registry push, SSH, …).
Bun (its home)
app.listen(3000) — the native Bun server. Sits at the raw Bun.serve ceiling (see benchmarks).
Every core: Bun is single-threaded per process — for multi-core boxes, spawn one process per core, each binding the same port with app.listen(PORT, { reusePort: true }); the kernel load-balances connections across them (Linux balances ~evenly). A supervisor that spawns + restarts workers is ~20 lines — see examples/cluster.ts. Anything shared across workers (rate limits, sessions, pub/sub) needs a shared store, as in any multi-instance deploy.
Node & Deno
@nifrajs/node—serve(app, { port })bridges tonode:http.@nifrajs/deno— servesapp.fetchonDeno.serve.
Self-hosting (no CDN in front)? Hand @nifrajs/node's serve a static mount and it serves the client bundle from disk — traversal-guarded, content-typed, with an immutable cache — before the app runs, leaving the SSR fast path untouched: serve(app, { port, static: { dir: new URL("./assets/", import.meta.url) } }). On Cloudflare/Vercel the platform serves assets, so you don't need it there.
Cloudflare Workers / Pages (the edge)
buildServer bundles with edge conditions (workerd, edge-light) into a _worker.js; toFetchHandler(app) is the handler. For Pages, a _routes.json serves the client bundle statically. SSR verified on real workerd — this very site runs there.
// _worker.ts — the edge SSR entry
import { toFetchHandler } from "@nifrajs/core"
import { createWebApp } from "@nifrajs/web"
import { reactAdapter } from "@nifrajs/web-react"
import { clientEntry, manifest } from "./server-manifest" // generated by buildServer
const app = createWebApp({ adapter: reactAdapter, manifest, clientEntry })
export default toFetchHandler(app) // a Workers/Pages fetch handler
// build.ts — buildClient (→ /assets) + buildServer (edge conditions → _worker.js)
import { buildClient, buildServer } from "@nifrajs/web/build"Deploy: wrangler pages deploy dist (Pages) or wrangler deploy (Workers). Deno Deploy and Vercel Edge reuse the same build with their conditions.