Hosting Providers

Zero-config web deployment with bini-deploy — generates hosting config and pushes straight to GitHub.

bini-deploy scans your project, generates the right hosting configuration for your target provider, and pushes it straight to GitHub — no YAML spelunking, no platform-specific docs to read first.

It's bundled into every Bini.js scaffold and exposed as npm run deploy. This page covers the web hosting providers it supports. For desktop and mobile targets, see Deployment Overview.

Installation

Already included in every Bini.js scaffold. To add it to an existing project:

npm install --save-dev bini-deploy

Supported Providers

ProviderRuntimeConfig generated
Node.js Node.js (default)Node (bini-server)None — bini-server handles it, bini-deploy just pushes to GitHub
Netlify NetlifyEdge Functions (Deno)netlify.toml
Vercel VercelNode.js Runtimevercel.json
Cloudflare Cloudflare WorkersWorkerswrangler.toml
Deno Deno DeployDenoserver/index.ts
Node is the default because Bini.js ships with bini-server, a zero-dependency production server. Choosing it skips config generation entirely — there's nothing to adapt, so bini-deploy just commits and pushes.

How it works

  1. Scan — scans src/app/api/ for route files
  2. Generate — creates the platform-specific entry file and config
  3. Clean — removes leftover entry files, config, and directories from any previously selected platform, including when switching to Node or a native platform
  4. Push — commits and pushes everything to your GitHub repository
  5. Deploy — your hosting provider deploys automatically from GitHub

Usage

Every Bini.js scaffold already has this wired into package.json, so deploying is just:

npm run deploy

This runs bini-deploy interactively — it prompts you to pick a platform (web, windows, macos, linux, android, ios) and, if you choose web, a hosting provider (node, netlify, vercel, cloudflare, deno).

For scripts and CI, skip the prompts with flags:

npx bini-deploy --platform web --hosting vercel --repo https://github.com/you/your-app --yes
FlagDescription
--platformweb, windows, macos, ios, linux, android
--hostingweb only — node (default), netlify, vercel, cloudflare, deno
--repoGitHub repository URL to push to
--generate-entrygenerate only the production entry file — netlify, vercel, cloudflare, deno
--yes, -yskip interactive prompts and use the flags provided

Node.jsNode.js

The default hosting choice. bini-server reads your API handlers directly from src/app/api/ at request time — no build step, no generated entry file.

npm run deploy

Pick web, then node (the default) when prompted.

Works out of the box on Railway, Render, Fly.io, or a bare VPS with pm2. See Production Server for the full runtime reference.

NetlifyNetlify

API routes run as Netlify Edge Functions.

npm run deploy

Pick web, then netlify when prompted.

Generates:

  • netlify.toml
  • netlify/edge-functions/api.ts
netlify.toml
[build]
  command = "vite build"
  publish = "dist"

[[edge_functions]]
  path = "/api/*"
  function = "api"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
Edge Functions run on Deno, not Node — packages depending on Node built-ins (fs, nodemailer) won't work there.

VercelVercel

API routes run on Vercel's Node.js Runtime.

npm run deploy

Pick web, then vercel when prompted.

Generates:

  • vercel.json
  • api/index.ts
Vercel reads api/index.ts before running your build — bini-deploy commits it for you, so it's already there when CI runs. It imports hono as an npm package, so bini-deploy checks it's installed and tells you the exact install command if it's missing.

CloudflareCloudflare Workers

API routes run as a Cloudflare Worker.

npm run deploy

Pick web, then cloudflare when prompted.

Generates:

  • wrangler.toml
  • worker.ts

Or deploy manually with Wrangler once the entry file exists:

npx wrangler deploy
Like Vercel, the worker entry imports hono as an npm package — bini-deploy verifies it's installed before generating the entry file.

DenoDeno Deploy

API routes run on Deno.

npm run deploy

Pick web, then deno when prompted.

Generates:

  • server/index.ts

In the Deno Deploy dashboard, set:

  • Entrypoint: server/index.ts
  • Runtime: Dynamic App
Deno Deploy also reads its entry file before building — same reasoning as Vercel above. Deno and Netlify both import hono directly from a URL, so no local install check is needed for these two.

API Routes & Hono

Every provider mounts the same file-based routes from src/app/api/, dynamic segments and catch-alls included:

src/app/api/
├── index.ts          → /api
├── users/
│   ├── index.ts       → /api/users
│   └── [id].ts        → /api/users/:id
└── posts/
    └── [...slug].ts    → /api/posts/*

Each route exports a default handler that accepts a Request and returns a Response (or a JSON-serializable value):

src/app/api/users/[id].ts
export default async function handler(req: Request) {
  const id = new URL(req.url).pathname.split('/').pop();
  return { id, name: 'Ada Lovelace' };
}

Imports from hono are detected automatically and mounted as a full Hono app instead:

src/app/api/hello/route.ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.json({ message: 'Hello from Hono!' }));
app.post('/', async (c) => {
  const body = await c.req.json();
  return c.json({ received: body });
});

export default app;
ESM projects: since Bini.js projects ship with "type": "module", Node's native ESM loader requires relative imports to include their file extension. bini-deploy's generated imports already include .js, but if your route files import local helpers (e.g. ./utils), include the extension there too (./utils.js) or the deployed function will crash with ERR_MODULE_NOT_FOUND even though the build succeeds.

Automatic CORS

API routes get permissive CORS headers out of the box on every non-Node hosting adapter — Netlify, Vercel, Cloudflare, and Deno — so your frontend can call them without extra setup.

Git Push Behavior

  • Existing remote — used without modification
  • New projects — the provided URL is added as origin
  • No remote updates — once a remote is set, it's never changed
  • Always main — always pushes to main, automatically renaming master if needed
  • Remote-ahead recovery — if the remote has commits you don't have locally (e.g. GitHub auto-created a README), bini-deploy fetches and merges automatically with --allow-unrelated-histories -X ours, keeping your local version of any file that exists on both sides. A warning prints before the merge runs; a real conflict stops the process and prints manual recovery steps
This means you can run bini-deploy multiple times without accidentally pushing to the wrong repository.

Troubleshooting

Build fails with Cannot read properties of undefined (reading 'readFile')

Your typescript dependency resolved to TypeScript 7.x, which shipped as a Go-native rewrite without a public compiler API. Some hosting-provider build pipelines break on it. Pin typescript to a ^6.x release in package.json instead of using latest.

RequirementVersion
Node.js>= 18
Vite>= 6
gitavailable on your PATH
GitHub repositorycreated ahead of time, to push to