smithery.ai

Frameworks Integration

Use when asking about "Hono", "itty-router", "routing frameworks", "middleware", "API framework", "web framework for Workers", "request routing", "Express-like", or choosing between web frameworks for Cloudflare Workers.

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version0.1.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,658 B
  • docs SUMMARY.md 250 B

History

  1. First recorded snapshot · 1 installs

SKILL.md

Web Frameworks for Workers

Purpose

This skill provides guidance on web frameworks for Cloudflare Workers, with focus on Hono and itty-router—the two most popular choices. Use this when building API endpoints, adding middleware, or choosing between vanilla fetch handlers and framework-based routing.

When to Use a Framework

Use a framework when:

  • Multiple routes/endpoints (>3-4)
  • Need middleware (auth, logging, CORS)
  • Building a REST API
  • Want type-safe routing
  • Team is familiar with Express-like patterns

Stay vanilla when:

  • Single endpoint or simple proxy
  • Maximum performance critical
  • Minimal dependencies desired
  • Learning Workers fundamentals

Framework Comparison

Feature Hono itty-router Vanilla
Bundle size ~14KB ~1KB 0KB
Type safety Excellent Good Manual
Middleware Built-in system Basic Manual
Learning curve Medium Low Low
Documentation Extensive Moderate N/A
Active development Very active Active N/A
Best for Full APIs Simple routing Single endpoint

Hono

Why Hono

  • Express-like API, easy to learn
  • Excellent TypeScript support
  • Rich middleware ecosystem
  • Built for edge runtimes
  • Active community and development

Basic Setup

import { Hono } from 'hono';

type Bindings = {
  DATABASE: D1Database;
  AI: Ai;
  CACHE: KVNamespace;
};

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

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

app.get('/api/users', async (c) => {
  const users = await c.env.DATABASE
    .prepare('SELECT * FROM users')
    .all();
  return c.json(users.results);
});

app.post('/api/users', async (c) => {
  const body = await c.req.json();
  // Validate and create user
  return c.json({ id: '123', ...body }, 201);
});

export default app;

Middleware

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { bearerAuth } from 'hono/bearer-auth';

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

// Global middleware
app.use('*', logger());
app.use('*', cors({
  origin: ['https://example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}));

// Route-specific middleware
app.use('/api/*', bearerAuth({ token: 'secret' }));

// Or custom middleware
app.use('/api/*', async (c, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  c.header('X-Response-Time', `${ms}ms`);
});

Route Groups

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

// API routes
const api = new Hono<{ Bindings: Bindings }>();
api.get('/users', usersHandler);
api.get('/users/:id', userByIdHandler);
api.post('/users', createUserHandler);

// Mount the group
app.route('/api', api);

// Or inline grouping
app.route('/api/v2', new Hono()
  .get('/health', (c) => c.json({ status: 'ok' }))
  .get('/version', (c) => c.json({ version: '2.0' }))
);

Error Handling

import { HTTPException } from 'hono/http-exception';

app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return c.json({ error: err.message }, err.status);
  }
  console.error('Unhandled error:', err);
  return c.json({ error: 'Internal server error' }, 500);
});

// Throwing HTTP exceptions
app.get('/api/users/:id', async (c) => {
  const id = c.req.param('id');
  const user = await getUser(id);

  if (!user) {
    throw new HTTPException(404, { message: 'User not found' });
  }

  return c.json(user);
});

With Workers AI

app.post('/api/chat', async (c) => {
  const { message } = await c.req.json();

  const response = await c.env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
    messages: [
      { role: 'system', content: 'You are a helpful assistant.' },
      { role: 'user', content: message }
    ]
  });

  return c.json({ response: response.response });
});

// Streaming response
app.post('/api/chat/stream', async (c) => {
  const { message } = await c.req.json();

  const stream = await c.env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
    messages: [{ role: 'user', content: message }],
    stream: true
  });

  return new Response(stream, {
    headers: { 'Content-Type': 'text/event-stream' }
  });
});

Validation with Zod

import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const createUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().positive().optional(),
});

app.post('/api/users',
  zValidator('json', createUserSchema),
  async (c) => {
    const user = c.req.valid('json');
    // user is typed and validated
    return c.json({ id: '123', ...user }, 201);
  }
);

itty-router

Why itty-router

  • Extremely lightweight (~1KB)
  • Dead simple API
  • No build step required
  • Good for simple APIs
  • Easy to understand source code

Basic Setup

import { Router } from 'itty-router';

interface Env {
  DATABASE: D1Database;
}

const router = Router();

router.get('/', () => new Response('Hello!'));

router.get('/api/users', async (request, env: Env) => {
  const users = await env.DATABASE
    .prepare('SELECT * FROM users')
    .all();
  return Response.json(users.results);
});

router.post('/api/users', async (request, env: Env) => {
  const body = await request.json();
  // Create user
  return Response.json({ id: '123', ...body }, { status: 201 });
});

// 404 fallback
router.all('*', () => new Response('Not Found', { status: 404 }));

export default {
  fetch: (request: Request, env: Env, ctx: ExecutionContext) =>
    router.handle(request, env, ctx)
};

With Middleware Pattern

import { Router } from 'itty-router';

const router = Router();

// Simple auth middleware
const withAuth = async (request: Request, env: Env) => {
  const token = request.headers.get('Authorization')?.replace('Bearer ', '');
  if (!token || token !== env.API_KEY) {
    return new Response('Unauthorized', { status: 401 });
  }
  // Don't return anything to continue to next handler
};

// Apply middleware to routes
router.get('/api/*', withAuth);
router.get('/api/data', (request, env) => Response.json({ data: 'secret' }));

Route Parameters

router.get('/api/users/:id', async (request, env) => {
  const { id } = request.params;
  const user = await env.DATABASE
    .prepare('SELECT * FROM users WHERE id = ?')
    .bind(id)
    .first();

  if (!user) {
    return new Response('Not Found', { status: 404 });
  }

  return Response.json(user);
});

// Optional parameters
router.get('/api/posts/:id?', (request) => {
  const { id } = request.params;
  if (id) {
    return Response.json({ post: id });
  }
  return Response.json({ posts: [] });
});

Choosing Between Them

Choose Hono When

  • Building a substantial API (>10 endpoints)
  • Need built-in middleware (CORS, auth, validation)
  • Want excellent TypeScript integration
  • Team comes from Express/Fastify background
  • Building something that will grow

Choose itty-router When

  • Simple API with few endpoints
  • Bundle size is critical
  • Want minimal abstraction
  • Quick prototype or proof of concept
  • Learning Workers fundamentals

Choose Vanilla When

  • Single-purpose Worker (proxy, redirect, etc.)
  • Maximum control needed
  • Zero dependencies required
  • Edge case the frameworks don't handle well

Vanilla Fetch Handler Pattern

For comparison, here's the vanilla approach:

interface Env {
  DATABASE: D1Database;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const path = url.pathname;
    const method = request.method;

    // Manual routing
    if (path === '/' && method === 'GET') {
      return new Response('Hello!');
    }

    if (path === '/api/users' && method === 'GET') {
      const users = await env.DATABASE
        .prepare('SELECT * FROM users')
        .all();
      return Response.json(users.results);
    }

    if (path.match(/^\/api\/users\/[\w-]+$/) && method === 'GET') {
      const id = path.split('/').pop();
      const user = await env.DATABASE
        .prepare('SELECT * FROM users WHERE id = ?')
        .bind(id)
        .first();

      if (!user) {
        return new Response('Not Found', { status: 404 });
      }
      return Response.json(user);
    }

    return new Response('Not Found', { status: 404 });
  }
};

Migration Patterns

From Vanilla to Hono

// Before (vanilla)
export default {
  async fetch(request: Request, env: Env) {
    if (request.method === 'GET' && new URL(request.url).pathname === '/') {
      return new Response('Hello');
    }
    return new Response('Not Found', { status: 404 });
  }
};

// After (Hono)
import { Hono } from 'hono';

const app = new Hono<{ Bindings: Env }>();
app.get('/', (c) => c.text('Hello'));
export default app;

From itty-router to Hono

// Before (itty-router)
import { Router } from 'itty-router';
const router = Router();
router.get('/api/users/:id', (req, env) => {
  const { id } = req.params;
  return Response.json({ id });
});

// After (Hono)
import { Hono } from 'hono';
const app = new Hono<{ Bindings: Env }>();
app.get('/api/users/:id', (c) => {
  const id = c.req.param('id');
  return c.json({ id });
});

Best Practices

Type Safety

Always type your bindings:

type Bindings = {
  DATABASE: D1Database;
  CACHE: KVNamespace;
  AI: Ai;
};

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

Error Handling

Always have a global error handler:

app.onError((err, c) => {
  console.error(err);
  return c.json({ error: 'Something went wrong' }, 500);
});

CORS

Configure CORS appropriately:

app.use('*', cors({
  origin: process.env.NODE_ENV === 'production'
    ? ['https://yourdomain.com']
    : ['http://localhost:3000'],
}));

Logging

Add request logging in development:

app.use('*', logger());  // Hono's built-in logger

Additional Resources