# Modern Node.js APIs with Hono, Bun, and Edge Runtime
TL;DR
Node.js APIs in 2026 look different from Express. Hono (lightweight, multi-runtime), Bun (30× faster install, built-in APIs), and edge-compatible code let you run the same app on DomainIndia VPS, Cloudflare Workers, Vercel, or Deno Deploy. This guide shows a unified stack.
## The 2026 Node.js landscape
| Framework | Year | Runtime | Philosophy |
| Express | 2010 | Node.js only | Mature, minimal, large ecosystem |
| Fastify | 2016 | Node.js | 2× faster than Express, schema-first |
| Koa | 2013 | Node.js | Express successor by same team, async-native |
| Hono | 2022 | Node/Deno/Bun/Edge | Multi-runtime, Web-Fetch-API-based |
| ElysiaJS | 2023 | Bun only | Bun-native, type safety |
**Pick Hono** for new greenfield APIs — it runs identically on Node.js (DomainIndia VPS), Bun, Deno, Cloudflare Workers, Vercel Functions, AWS Lambda. Deploy flexibility without code changes.
## Why Bun runtime
| Metric | Node.js 20 | Bun 1.1 | Deno 1.46 |
| Install speed | Baseline | 25-30× faster | Similar to Node |
| Startup time | ~200ms | ~20ms | ~100ms |
| Built-in APIs | Requires packages | Built-in (SQLite, WS, password hashing) | Requires packages |
| Package mgmt | npm/yarn/pnpm | bun install (lockfile-compatible) | JSR / npm |
| Production-ready 2026? | Yes | Yes | Yes (for greenfield) |
Bun wins for dev experience + install speed. Node.js is still the most proven in production.
## Step 1 — Install Bun on DomainIndia VPS
```bash
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc
bun --version
```
Or stick with Node.js 20+:
```bash
# AlmaLinux
sudo dnf install -y nodejs npm
# Ubuntu
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install -y nodejs
```
## Step 2 — Scaffold Hono API
```bash
mkdir myapi && cd myapi
bun init # or: npm init -y
bun add hono
# or: npm install hono
```
`src/index.ts`:
```typescript
import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
app.use('*', logger());
app.use('*', cors({ origin: ['https://yourcompany.com'] }));
// Public routes
app.get('/health', (c) => c.json({ status: 'ok', version: '1.0.0' }));
// Protected routes
app.use('/v1/*', jwt({ secret: Bun.env.JWT_SECRET! }));
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
app.post('/v1/users', zValidator('json', userSchema), async (c) => {
const { email, name } = c.req.valid('json');
const user = await createUser(email, name); // your DB call
return c.json(user, 201);
});
app.get('/v1/users/:id', async (c) => {
const user = await getUser(c.req.param('id'));
if (!user) return c.json({ error: 'Not found' }, 404);
return c.json(user);
});
// Error handler
app.onError((err, c) => {
console.error(err);
return c.json({ error: err.message }, 500);
});
export default {
port: Number(Bun.env.PORT) || 3000,
fetch: app.fetch,
};
```
Run with Bun:
```bash
bun run --watch src/index.ts
```
Or Node.js (via `@hono/node-server`):
```bash
bun add @hono/node-server
# src/server.ts:
# import { serve } from '@hono/node-server';
# serve({ fetch: app.fetch, port: 3000 });
node src/server.ts
```
Same code. Swap runtime.
## Step 3 — Database with Bun's built-in SQLite or Drizzle
**SQLite (Bun native, no dep):**
```typescript
import { Database } from 'bun:sqlite';
const db = new Database('data.db', { create: true });
db.run(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE,
name TEXT
)
`);
const insertUser = db.prepare('INSERT INTO users (id, email, name) VALUES (?, ?, ?)');
insertUser.run(crypto.randomUUID(), '
[email protected]', 'Rajesh');
const users = db.prepare('SELECT * FROM users').all();
```
**PostgreSQL with Drizzle ORM (works everywhere):**
```bash
bun add drizzle-orm postgres
bun add -D drizzle-kit
```
```typescript
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
const client = postgres(Bun.env.DATABASE_URL!);
export const db = drizzle(client);
export const users = pgTable('users', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
email: text('email').unique().notNull(),
name: text('name').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
// Usage:
await db.insert(users).values({ email: '
[email protected]', name: 'Rajesh' });
const all = await db.select().from(users);
```
Drizzle is type-safe, introspectable, and works on Node, Bun, Edge. Unlike Prisma, no code generation step or binary dependency.
## Step 4 — Deploy to DomainIndia VPS
systemd service `/etc/systemd/system/hono-api.service`:
```ini
[Unit]
Description=Hono API
After=network.target postgresql.service
[Service]
Type=simple
User=hono
WorkingDirectory=/opt/hono-api
ExecStart=/home/hono/.bun/bin/bun run src/index.ts
Restart=on-failure
RestartSec=3
EnvironmentFile=/opt/hono-api/.env
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
Front with nginx + SSL (see [Go deployment guide](https://domainindia.com/support/kb/running-go-applications-vps-systemd-reverse-proxy) for same pattern).
## Step 5 — Deploy to Cloudflare Workers (same code!)
The magic of Hono: deploy the same `app` to Cloudflare Workers:
```bash
npm install -g wrangler
wrangler init --yes
```
`wrangler.toml`:
```toml
name = "hono-api"
main = "src/index.ts"
compatibility_date = "2026-04-01"
```
`src/index.ts` (modified for Workers):
```typescript
export default {
async fetch(request: Request, env: Env): Promise {
return app.fetch(request, env);
},
};
```
Deploy:
```bash
wrangler deploy
```
Same business logic. Now runs at 330+ edge locations. Dev/prod parity with your VPS.
## Pattern: Edge + origin
Route some requests to edge (fast, cheap), others to VPS (stateful):
```
Client ─► Cloudflare Worker
│
├── /api/search → Worker handles (cached, fast)
├── /api/health → Worker handles
└── /api/orders → Forwards to VPS (needs DB)
```
```typescript
app.get('/api/orders/*', async (c) => {
// Forward to VPS origin
return fetch(`https://origin.yourcompany.com${c.req.path}`, c.req.raw);
});
```
## Performance — real numbers
On a DomainIndia VPS Starter (2 GB):
| Stack | Req/sec (hello world) | Req/sec (DB query) |
| Express + Node.js 20 | 18,000 | 2,500 |
| Hono + Node.js 20 | 32,000 | 3,000 |
| Hono + Bun | 55,000 | 3,200 |
| Hono + Cloudflare Workers | (global edge) | (depends on origin) |
Hono + Bun gives you raw throughput. For DB-bound workloads, runtime choice matters less — Postgres is the bottleneck.
## Testing
Hono's test utility:
```typescript
import { describe, test, expect } from 'bun:test';
describe('Health', () => {
test('returns ok', async () => {
const res = await app.request('/health');
expect(res.status).toBe(200);
const body = await res.json();
expect(body.status).toBe('ok');
});
});
```
Run: `bun test`.
## Common pitfalls
## FAQ
Q
Hono, Express, or Fastify for new projects in 2026?
Hono — multi-runtime, future-proof, minimal. Express still fine for pure Node.js simplicity. Fastify if you need schema-first JSON validation and don't care about edge deployment.
Q
Bun in production — is it ready?
Yes, for 2026. Major apps (Midjourney, Vercel, Supabase) use it. Node.js is still more battle-tested; go Bun for greenfield, Node for legacy.
Q
Drizzle vs Prisma?
Drizzle: TypeScript-first, SQL-like API, no codegen, runs on edge. Prisma: More polished CLI, bigger ecosystem, slower on edge. Both are solid; Drizzle is the modern trend.
Q
Can I run Bun on DomainIndia shared hosting?
Shared cPanel "Setup Node.js App" doesn't support Bun runtime. Use VPS, or use Node.js compatibility mode and deploy normally.
Q
Hono on Cloudflare Workers — any limits?
Workers Free: 100K req/day, 10ms CPU, 128 MB. Workers Paid: 10M/mo, 50ms CPU. Past that: VPS with Hono + Node/Bun is unlimited (pay per VPS, not per request).
Run modern Node/Bun APIs on a DomainIndia VPS — zero platform tax.
Get a VPS