Guidelines for building edge-first, high-performance APIs with Hono and TypeScript for Cloudflare Workers, Deno, Bun, and Node.js
Hono TypeScript Development
You are an expert in Hono and TypeScript development with deep knowledge of building ultrafast, edge-first APIs that run on Cloudflare Workers, Deno, Bun, and Node.js.
TypeScript General Guidelines
Basic Principles
Use English for all code and documentation
Always declare types for variables and functions (parameters and return values)
Avoid using any type - create necessary types instead
Use JSDoc to document public classes and methods
Write concise, maintainable, and technically accurate code
Use functional and declarative programming patterns; avoid classes
Prefer iteration and modularization to adhere to DRY principles
Nomenclature
Use PascalCase for types and interfaces
Use camelCase for variables, functions, and methods
Use kebab-case for file and directory names
Use UPPERCASE for environment variables
Use descriptive variable names with auxiliary verbs: isLoading, hasError, canDelete
Start each function with a verb
Functions
Write short functions with a single purpose
Use arrow functions for handlers and middleware
Prefer the RO-RO pattern: Receive an Object, Return an Object
Use default parameters instead of null checks
Types and Interfaces
Prefer interfaces over types for object shapes
Avoid enums; use maps or const objects instead for better type safety
Use Zod for runtime validation with inferred types
Use readonly for immutable properties
Use import type for type-only imports
Hono-Specific Guidelines
Project Structure
src/
routes/
{resource}/
index.ts
handlers.ts
validators.ts
middleware/
auth.ts
cors.ts
logger.ts
services/
{domain}Service.ts
types/
index.ts
utils/
config/
index.ts
App Initialization
import { Hono } from 'hono';
// Type your environment bindings
type Bindings = {
DB: D1Database;
KV: KVNamespace;
JWT_SECRET: string;
};
type Variables = {
user: User;
};
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
Routing
Use method chaining for clean route definitions
Group related routes with app.route()
Use route parameters with proper typing
const users = new Hono<{ Bindings: Bindings }>();
users.get('/', listUsers);
users.get('/:id', getUser);
users.post('/', zValidator('json', createUserSchema), createUser);
users.put('/:id', zValidator('json', updateUserSchema), updateUser);
users.delete('/:id', deleteUser);
app.route('/api/users', users);
Middleware
Use Hono's built-in middleware where available
Create typed middleware for custom logic
Chain middleware for composability
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { jwt } from 'hono/jwt';
app.use('*', logger());
app.use('/api/*', cors());
app.use('/api/*', jwt({ secret: 'your-secret' }));
// Custom middleware
const authMiddleware = async (c: Context, next: Next) => {
const user = await validateUser(c);
c.set('user', user);
await next();
};
Request Validation with Zod
Use @hono/zod-validator for request validation
Define schemas for all request inputs
Infer types from Zod schemas
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
role: z.enum(['user', 'admin']).default('user'),
});
type CreateUserInput = z.infer<typeof createUserSchema>;
app.post('/users', zValidator('json', createUserSchema), async (c) => {
const data = c.req.valid('json');
// data is typed as CreateUserInput
});
Context and Response Handling
Use typed context for better type safety
Use helper methods for responses: c.json(), c.text(), c.html()
Access environment bindings through context
app.get('/users/:id', async (c) => {
const id = c.req.param('id');
const db = c.env.DB;
const user = await db.prepare('SELECT * FROM users WHERE id = ?')
.bind(id)
.first();
if (!user) {
return c.json({ error: 'User not found' }, 404);
}
return c.json(user);
});
Error Handling
Use Hono's HTTPException for expected errors
Create global error handler middleware
Return consistent error responses
import { HTTPException } from 'hono/http-exception';
// Throwing errors
if (!user) {
throw new HTTPException(404, { message: 'User not found' });
}
// Global error handler
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: err.message }, err.status);
}
console.error(err);
return c.json({ error: 'Internal Server Error' }, 500);
});
Cloudflare Workers Integration
Use Workers KV for key-value storage
Use D1 for SQL databases
Use R2 for object storage
Use Durable Objects for stateful applications
// D1 Database
const result = await c.env.DB.prepare('SELECT * FROM users').all();
// KV Storage
await c.env.KV.put('key', 'value');
const value = await c.env.KV.get('key');
// R2 Storage
await c.env.BUCKET.put('file.txt', content);
Testing
Use Hono's test client for integration tests
Use Vitest or Jest as test runner
Test handlers and middleware separately
import { testClient } from 'hono/testing';
import { describe, it, expect } from 'vitest';
describe('User API', () => {
const client = testClient(app);
it('should list users', async () => {
const res = await client.api.users.$get();
expect(res.status).toBe(200);
const data = await res.json();
expect(Array.isArray(data)).toBe(true);
});
});
Performance
Hono is ultrafast with minimal overhead
Use streaming responses for large data
Leverage edge caching with Cache API
Use hono/tiny preset for minimal bundle size
Security
Use hono/secure-headers middleware
Implement rate limiting
Validate all inputs with Zod
Use JWT for authentication
Enable CORS appropriately
Multi-Runtime Support
Hono runs on multiple runtimes. Configure appropriately:
// Cloudflare Workers
export default app;
// Node.js
import { serve } from '@hono/node-server';
serve(app);
// Bun
export default app;
// Deno
Deno.serve(app.fetch);don't have the plugin yet? install it then click "run inline in claude" again.