Best Hono Backend Boilerplates 2026
·StarterPick Team
honobackendapicloudflare-workersbuntypescript2026
TL;DR
Hono's official starter templates are the best starting point. create-hono generates a project configured for your target runtime (Node.js, Cloudflare Workers, Bun, AWS Lambda, Deno) in under a minute. Community starters add Drizzle/Prisma, JWT auth, and OpenAPI specs. The Hono ecosystem has matured quickly — you get a full API stack (routing, validation, middleware, RPC types) without the weight of NestJS or the limitations of tRPC.
Key Takeaways
- create-hono: official scaffold, supports 8 runtimes, TypeScript first
- hono-zod-openapi: community starter with Zod validation + auto-generated OpenAPI docs
- Hono + Drizzle: common combo for serverless APIs (both edge-compatible)
- Hono RPC: type-safe client without a separate schema layer (like tRPC but built-in)
- Edge deployment: Cloudflare Workers starter is the most popular — zero cold starts
- For SaaS backends: Node.js adapter + Drizzle + Hono RPC = full type-safe API
Official: create-hono
npm create hono@latest my-api
# Prompts:
# ✔ Which template do you want to use?
# › cloudflare-workers
# nodejs
# bun
# aws-lambda
# deno
# vercel
# lambda-edge
# fastly
Cloudflare Workers Template
my-api/
├── src/
│ └── index.ts ← Hono app
├── wrangler.toml ← Cloudflare Workers config
├── package.json
└── tsconfig.json
// src/index.ts — Cloudflare Workers starter:
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
type Bindings = {
DATABASE_URL: string;
JWT_SECRET: string;
// Cloudflare bindings: KV, D1, R2, etc.
CACHE: KVNamespace;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use('*', cors());
app.use('*', logger());
app.get('/', (c) => c.json({ message: 'Hello from Hono!' }));
app.get('/health', (c) => {
return c.json({
status: 'ok',
runtime: 'cloudflare-workers',
timestamp: new Date().toISOString(),
});
});
export default app;
# wrangler.toml:
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-id"
[vars]
DATABASE_URL = "your-db-url"
Node.js Template
// src/index.ts — Node.js server:
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
app.get('/', (c) => c.text('Hello Node.js!'));
const port = Number(process.env.PORT ?? 3000);
serve({ fetch: app.fetch, port }, (info) => {
console.log(`Listening on http://localhost:${info.port}`);
});
Community Starter: Hono + Drizzle + Auth
A popular community pattern for full-featured Hono APIs:
# Manual setup (no dedicated repo — copy this pattern):
npm install hono @hono/node-server @hono/zod-validator
npm install drizzle-orm @auth/core hono-auth-helpers
npm install --save-dev drizzle-kit tsx
// src/db/schema.ts:
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
email: text('email').notNull().unique(),
name: text('name'),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const sessions = pgTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id').notNull().references(() => users.id),
expiresAt: timestamp('expires_at').notNull(),
});
// src/routes/posts.ts — typed route handlers:
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { jwt } from 'hono/jwt';
import { z } from 'zod';
import { db } from '../db';
import { posts } from '../db/schema';
import { eq, desc } from 'drizzle-orm';
const postsRouter = new Hono()
.use('*', jwt({ secret: process.env.JWT_SECRET! }))
.get('/', async (c) => {
const allPosts = await db.select().from(posts).orderBy(desc(posts.createdAt)).limit(20);
return c.json(allPosts);
})
.post('/', zValidator('json', z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
})), async (c) => {
const { title, content } = c.req.valid('json');
const payload = c.get('jwtPayload');
const [post] = await db.insert(posts).values({
title,
content,
authorId: payload.sub,
}).returning();
return c.json(post, 201);
});
export { postsRouter };
// src/index.ts — assemble routes:
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { cors } from 'hono/cors';
import { postsRouter } from './routes/posts';
import { authRouter } from './routes/auth';
const app = new Hono()
.use('*', cors({ origin: process.env.FRONTEND_URL }))
.route('/auth', authRouter)
.route('/api/posts', postsRouter);
// Export type for RPC client:
export type AppType = typeof app;
serve({ fetch: app.fetch, port: 3000 });
Hono OpenAPI Starter
For public APIs needing documentation:
npm install @hono/zod-openapi @hono/swagger-ui
// OpenAPI route with auto-generated docs:
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
const app = new OpenAPIHono();
const PostSchema = z.object({
id: z.string().openapi({ example: 'post-123' }),
title: z.string().openapi({ example: 'Hello World' }),
content: z.string(),
});
const createPostRoute = createRoute({
method: 'post',
path: '/posts',
request: {
body: { content: { 'application/json': { schema: PostSchema.omit({ id: true }) } } },
},
responses: {
201: { description: 'Post created', content: { 'application/json': { schema: PostSchema } } },
400: { description: 'Validation error' },
},
tags: ['posts'],
summary: 'Create a new post',
});
app.openapi(createPostRoute, async (c) => {
const body = c.req.valid('json');
const post = await db.insert(posts).values(body).returning();
return c.json(post[0], 201);
});
// Auto-generate OpenAPI spec at /doc:
app.doc('/doc', { openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } });
// Swagger UI at /ui:
app.get('/ui', swaggerUI({ url: '/doc' }));
Hono RPC Client (Type-Safe, No Schema)
// Frontend/client — type-safe without a separate schema layer:
import { hc } from 'hono/client';
import type { AppType } from '../server/src/index'; // Import server types
const client = hc<AppType>(process.env.NEXT_PUBLIC_API_URL!);
// Fully typed — no codegen needed:
const postsResponse = await client.api.posts.$get();
const posts = await postsResponse.json(); // typed: Post[]
const newPost = await client.api.posts.$post({
json: { title: 'Hello', content: 'World' },
});
Starter Templates by Use Case
| Use Case | Template | Key Packages |
|---|---|---|
| Cloudflare Workers API | create-hono (cf) | Hono + Cloudflare D1 + Drizzle |
| Node.js REST API | create-hono (nodejs) | Hono + Drizzle + JWT |
| Bun API | create-hono (bun) | Hono + Bun + Drizzle |
| OpenAPI service | Manual | Hono + zod-openapi |
| Full-stack Next.js backend | Manual | Hono + Next.js + Hono RPC |
Choose Hono boilerplate if:
→ Building a standalone API (not tRPC)
→ Deploying to Cloudflare Workers or edge
→ Need OpenAPI spec generation
→ Want type-safe client without full tRPC setup
→ Migrating from Express
Prefer tRPC (T3 Stack) if:
→ Next.js full-stack app
→ Server and client in same monorepo
→ Team knows T3 patterns
Find Hono boilerplates and compare backend starters at StarterPick.