Skip to main content
โšก Calmops

Hono: The Ultrafast Web Framework for Edge

Hono is a small, simple, and ultrafast web framework that works on any JavaScript runtime. It’s designed specifically for edge computing but works anywhere. This comprehensive guide covers everything you need to know.

What is Hono?

Hono is a web framework built for the edge with a unique “Write Once, Run Anywhere” philosophy.

import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello Hono!'));

export default app;

Key Features

  • Multi-runtime - Cloudflare Workers, Deno, Bun, Node.js
  • Ultrafast - Minimal overhead, maximum performance
    • Middleware - Rich ecosystem of middleware
    • TypeScript - First-class TypeScript support
    • Tiny - Only ~12KB
    • RESTful - Full HTTP router included

Basic Usage

Cloudflare Workers

import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello Cloudflare!'));

app.get('/api/users', (c) => {
  return c.json({
    users: [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ]
  });
});

export default app;

Deno

import { Hono } from 'https://deno.land/x/hono/mod.ts';

const app = new Hono();

app.get('/', (c) => c.text('Hello Deno!'));

Deno.serve(app.fetch);

Bun

import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello Bun!'));

export default {
  port: 3000,
  fetch: app.fetch
};

Node.js

import { Hono } from 'hono';
import { serve } from '@hono/node-server';

const app = new Hono();

app.get('/', (c) => c.text('Hello Node!'));

serve(app);

Routing

Basic Routes

const app = new Hono();

// HTTP methods
app.get('/users', (c) => c.text('GET users'));
app.post('/users', (c) => c.text('POST users'));
app.put('/users/:id', (c) => c.text('PUT user'));
app.delete('/users/:id', (c) => c.text('DELETE user'));

// All methods
app.all('/api', (c) => c.text('Any method'));

Route Parameters

app.get('/users/:id', (c) => {
  const id = c.req.param('id');
  return c.text(`User ID: ${id}`);
});

// Multiple params
app.get('/posts/:postId/comments/:commentId', (c) => {
  const { postId, commentId } = c.req.param();
  return c.json({ postId, commentId });
});

Query Parameters

app.get('/search', (c) => {
  const query = c.req.query('q');
  const page = c.req.query('page') || '1';
  const limit = c.req.query('limit') || '10';
  
  return c.json({ query, page, limit });
});

// Get all queries as object
app.get('/filter', (c) => {
  const queries = c.req.query();
  return c.json(queries);
});

Nested Routes

const app = new Hono();

const api = app.route('/api');
const v1 = api.route('/v1');

v1.get('/users', (c) => c.json({ users: [] }));
v1.get('/posts', (c) => c.json({ posts: [] }));

Context Object

Request Information

app.get('/info', (c) => {
  // Path and method
  c.req.path;      // '/info'
  c.req.method;   // 'GET'
  
  // URL
  c.req.url;      // 'http://localhost:3000/info?q=hello'
  c.req.query();  // { q: 'hello' }
  c.req.param();  // { id: '123' }
  
  // Headers
  c.req.header('Authorization');
  c.req.headers(); // All headers
  
  // Body
  c.req.parseBody(); // Parse JSON/form data
  c.req.text();      // Raw text
  c.req.json();      // Parse JSON
  
  // Raw Request (runtime-specific)
  c.req.raw; // Request object
  
  return c.text('Hello');
});

Response Methods

app.get('/response', (c) => {
  // Text response
  return c.text('Plain text');
  
  // JSON response
  return c.json({ message: 'JSON' });
  
  // HTML response
  return c.html('<h1>HTML</h1>');
  
  // Custom status
  return c.text('Created', 201);
  // or
  return c.json({ error: 'Not found' }, 404);
  
  // Headers
  return c.text('With header', 200, {
    'X-Custom': 'value'
  });
  
  // Redirect
  return c.redirect('/other');
  
  // Cookie
  c.cookie('token', 'abc123');
  return c.text('Cookie set');
});

Middleware

Built-in Middleware

import { 
  cors, 
  logger, 
  poweredBy,
  timeout,
  compress
} from 'hono/middleware';

const app = new Hono();

// CORS
app.use('/*', cors());

// Add logger
app.use('/*', logger());

// Add powered by header
app.use('/*', poweredBy());

// Response compression
app.use('/*', compress());

// Timeout (in milliseconds)
app.use('/*', timeout(5000));

Custom Middleware

// Method 1: Using app.use()
app.use('/*', async (c, next) => {
  const start = Date.now();
  await next();
  const end = Date.now();
  c.res.headers.set('X-Response-Time', `${end - start}ms`);
});

// Method 2: Using function
const timing = () => async (c, next) => {
  const start = Date.now();
  await next();
  c.res.headers.set('X-Process-Time', `${Date.now() - start}ms`);
};

app.use('/*', timing());

Third-party Middleware

import { jwt } from 'hono/jwt';
import { rateLimit } from 'hono/rate-limit';
import { secureHeaders } from 'hono/secure-headers';

// JWT Authentication
app.use('/api/*', jwt({
  secret: 'my-secret-key'
}));

// Rate Limiting
app.use('/*', rateLimit({
  limit: 100,
  window: 60000
}));

// Security Headers
app.use('/*', secureHeaders());

Working with JSON

Parsing Request Body

app.post('/users', async (c) => {
  const body = await c.req.json();
  
  // With type safety
  // const body = await c.req.json<{ name: string; email: string }>();
  
  return c.json({ received: body }, 201);
});

Returning Responses

app.get('/data', (c) => {
  const data = {
    users: [
      { id: 1, name: 'John' }
    ],
    meta: {
      total: 1,
      page: 1
    }
  };
  
  return c.json(data);
});

Error Handling

Handling Errors

app.get('/error', (c) => {
  throw new Error('Something went wrong');
});

// Global error handler
app.onError((err, c) => {
  console.error(err);
  return c.json({ 
    error: err.message 
  }, 500);
});

// 404 handler
app.notFound((c) => {
  return c.json({ 
    error: 'Not Found' 
  }, 404);
});

Building an API

Complete Example

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

const app = new Hono();

// Middleware
app.use('/*', cors());
app.use('/*', logger());

// In-memory database
const db = {
  users: [
    { id: '1', name: 'John', email: '[email protected]' },
    { id: '2', name: 'Jane', email: '[email protected]' }
  ]
};

// Routes
app.get('/', (c) => c.json({ 
  message: 'Welcome to the API',
  version: '1.0'
}));

app.get('/users', (c) => {
  const { limit, offset } = c.req.query();
  const limitNum = parseInt(limit) || 10;
  const offsetNum = parseInt(offset) || 0;
  
  return c.json({
    users: db.users.slice(offsetNum, offsetNum + limitNum),
    total: db.users.length
  });
});

app.get('/users/:id', (c) => {
  const id = c.req.param('id');
  const user = db.users.find(u => u.id === id);
  
  if (!user) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  return c.json(user);
});

app.post('/users', async (c) => {
  const body = await c.req.json();
  
  if (!body.name || !body.email) {
    return c.json({ error: 'Name and email required' }, 400);
  }
  
  const newUser = {
    id: String(db.users.length + 1),
    name: body.name,
    email: body.email
  };
  
  db.users.push(newUser);
  
  return c.json(newUser, 201);
});

app.put('/users/:id', async (c) => {
  const id = c.req.param('id');
  const body = await c.req.json();
  
  const index = db.users.findIndex(u => u.id === id);
  
  if (index === -1) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  db.users[index] = { ...db.users[index], ...body };
  
  return c.json(db.users[index]);
});

app.delete('/users/:id', (c) => {
  const id = c.req.param('id');
  const index = db.users.findIndex(u => u.id === id);
  
  if (index === -1) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  db.users.splice(index, 1);
  
  return c.text('', 204);
});

export default app;

With TypeScript

Type Safety

import { Hono } from 'hono';
import type { Context, Next } from 'hono';

type Env = {
  DB: D1Database;
};

type Variables = {
  userId: string;
};

const app = new Hono<{ 
  Variables: Variables;
  Bindings: Env;
}>();

// Access variables with type safety
app.get('/api', (c) => {
  const userId = c.get('userId');
  return c.json({ userId });
});

// Type-safe request parsing
app.post('/users', async (c) => {
  const body = await c.req.json<{
    name: string;
    email: string;
  }>();
  
  return c.json({ name: body.name });
});

Integration Examples

With Cloudflare Workers

import { Hono } from 'hono';
import { cors } from 'hono/cors';

const app = new Hono();

app.use('/*', cors());

app.get('/kv/:key', async (c) => {
  const key = c.req.param('key');
  const value = await c.env.KV.get(key);
  return c.json({ key, value });
});

app.post('/kv/:key', async (c) => {
  const key = c.req.param('key');
  const { value } = await c.req.json();
  await c.env.KV.put(key, value);
  return c.text('Created', 201);
});

export default app;

With Deno Deploy

import { Hono } from 'https://deno.land/x/hono/mod.ts';
import { cors } from 'https://deno.land/x/hono/middleware.ts';

const app = new Hono();

app.use('/*', cors());

app.get('/deno', (c) => c.text('Hello Deno Deploy!'));

Deno.serve(app.fetch);

With React Frontend

// api.js - Hono backend
import { Hono } from 'hono';

const app = new Hono();

app.post('/api/contact', async (c) => {
  const body = await c.req.json();
  // Process form submission
  return c.json({ success: true });
});

export default app;
// React frontend
function ContactForm() {
  const [submitted, setSubmitted] = useState(false);
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData);
    
    await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    
    setSubmitted(true);
  };
  
  return submitted 
    ? <p>Thank you!</p>
    : <form onSubmit={handleSubmit}>...</form>;
}

Performance

Hono is designed for maximum performance on edge runtimes.

Runtime Requests/sec
Cloudflare Workers ~180K
Bun ~200K
Deno ~150K
Node.js ~120K

External Resources

Conclusion

Hono is an excellent choice for building fast web applications, especially for edge deployment. Key points:

  • Works on any JavaScript runtime (Cloudflare, Deno, Bun, Node)
  • Simple and intuitive API
  • Rich middleware ecosystem
  • Excellent TypeScript support
  • Tiny footprint with minimal overhead

For edge-first applications or multi-runtime projects, Hono provides a consistent development experience.

Comments