Claude
Skills
Sign in
Back

hono-core

Included with Lifetime
$97 forever

Hono ultrafast web framework fundamentals - routing, context, handlers, and response patterns for multi-runtime deployment

Generalhonoweb-frameworkroutingtypescriptcloudflare-workersdenobunnodejs

What this skill does


# Hono - Ultrafast Web Framework

## Overview

Hono is a small, simple, and ultrafast web framework built on Web Standards. It runs on Cloudflare Workers, Deno, Bun, Node.js, and more with the same codebase. The name means "flame" in Japanese.

**Key Features**:
- Built on Web Standards (Request/Response/fetch)
- Multi-runtime: Cloudflare Workers, Deno, Bun, Node.js, Vercel, AWS Lambda
- Ultrafast routing with RegExpRouter
- First-class TypeScript support
- Lightweight (~14KB minified)
- Rich middleware ecosystem

**Installation**:
```bash
# Create new project (recommended)
npm create hono@latest my-app

# Or install in existing project
npm install hono

# Runtime-specific adapters
npm install @hono/node-server  # Node.js
```

## When to Use This Skill

Use Hono when:
- Building APIs for edge/serverless environments (Cloudflare Workers, Vercel Edge)
- Need multi-runtime portability (same code on Bun, Deno, Node.js)
- Want TypeScript-first development with excellent type inference
- Building lightweight, high-performance APIs
- Need built-in middleware for common patterns (CORS, auth, compression)

**Hono vs Other Frameworks**:
- **Hono**: Multi-runtime, Web Standards, ultrafast, edge-optimized
- **Express**: Node.js only, larger ecosystem, slower
- **Fastify**: Node.js only, schema-based, good performance
- **Elysia**: Bun only, excellent performance, different API style

## Core Concepts

### Creating an Application

```typescript
import { Hono } from 'hono'

const app = new Hono()

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

export default app
```

**With TypeScript Generics** (for bindings/variables):
```typescript
type Bindings = {
  DATABASE_URL: string
  API_KEY: string
}

type Variables = {
  user: { id: string; name: string }
}

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

### The Context Object (c)

The context `c` provides access to request data and response methods:

```typescript
app.get('/users/:id', async (c) => {
  // Request data
  const id = c.req.param('id')           // Path parameter
  const query = c.req.query('sort')       // Query parameter ?sort=asc
  const queries = c.req.queries('tags')   // Multiple: ?tags=a&tags=b
  const header = c.req.header('Authorization')
  const body = await c.req.json()         // JSON body
  const form = await c.req.formData()     // Form data

  // Environment (Cloudflare Workers bindings)
  const db = c.env.DATABASE_URL

  // Custom variables (set by middleware)
  const user = c.get('user')

  // Response methods
  return c.text('Plain text')
  return c.json({ id, name: 'User' })
  return c.html('<h1>Hello</h1>')
  return c.redirect('/login')
  return c.notFound()
})
```

### Response Methods

```typescript
// Text response
c.text('Hello', 200)

// JSON response
c.json({ message: 'Success' }, 201)
c.json({ error: 'Not found' }, 404)

// HTML response
c.html('<h1>Hello</h1>')

// Redirect
c.redirect('/login')           // 302 default
c.redirect('/login', 301)      // Permanent redirect

// Headers
c.header('X-Custom', 'value')
c.header('Cache-Control', 'max-age=3600')

// Streaming
c.streamText(async (stream) => {
  await stream.write('Hello ')
  await stream.write('World!')
})

// Raw Response
return new Response('Raw', { status: 200 })
```

## Routing Patterns

### Basic Routing

```typescript
const app = new Hono()

// HTTP methods
app.get('/users', getUsers)
app.post('/users', createUser)
app.put('/users/:id', updateUser)
app.delete('/users/:id', deleteUser)
app.patch('/users/:id', patchUser)

// All methods
app.all('/webhook', handleWebhook)

// Custom methods
app.on('PURGE', '/cache', purgeCache)
app.on(['GET', 'POST'], '/form', handleForm)
```

### Path Parameters

```typescript
// Single parameter
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id })
})

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

// Optional parameter
app.get('/api/animal/:type?', (c) => {
  const type = c.req.param('type') || 'all'
  return c.json({ type })
})

// Regex validation
app.get('/posts/:id{[0-9]+}', (c) => {
  const id = c.req.param('id')  // Only numeric IDs
  return c.json({ id })
})

// Wildcards
app.get('/files/*', (c) => {
  const path = c.req.param('*')  // Everything after /files/
  return c.text(`File: ${path}`)
})
```

### Route Grouping

```typescript
// Using app.route()
const api = new Hono()
api.get('/users', getUsers)
api.get('/posts', getPosts)

const app = new Hono()
app.route('/api/v1', api)  // /api/v1/users, /api/v1/posts

// Using basePath()
const v2 = new Hono().basePath('/api/v2')
v2.get('/users', getUsers)  // /api/v2/users

// Chaining
app
  .get('/a', handlerA)
  .post('/b', handlerB)
  .delete('/c', handlerC)
```

### Route Organization (Multi-File)

```typescript
// routes/users.ts
import { Hono } from 'hono'

const users = new Hono()

users.get('/', async (c) => {
  return c.json({ users: [] })
})

users.post('/', async (c) => {
  const body = await c.req.json()
  return c.json({ created: body }, 201)
})

users.get('/:id', async (c) => {
  const id = c.req.param('id')
  return c.json({ id })
})

export default users

// app.ts
import { Hono } from 'hono'
import users from './routes/users'
import posts from './routes/posts'

const app = new Hono()

app.route('/users', users)
app.route('/posts', posts)

export default app
```

## Handler Patterns

### Inline Handlers

```typescript
// Simple handler
app.get('/hello', (c) => c.text('Hello!'))

// Async handler
app.get('/users', async (c) => {
  const users = await fetchUsers()
  return c.json({ users })
})

// Multiple handlers (middleware chain)
app.get('/admin', authenticate, authorize, (c) => {
  return c.json({ admin: true })
})
```

### Using Factory for Type-Safe Handlers

```typescript
import { createFactory } from 'hono/factory'

const factory = createFactory<{ Bindings: Bindings }>()

// Create typed handler
const getUser = factory.createHandlers(async (c) => {
  const id = c.req.param('id')
  const db = c.env.DATABASE_URL  // Typed!
  return c.json({ id })
})

app.get('/users/:id', ...getUser)
```

## Error Handling

### Built-in Error Handling

```typescript
import { HTTPException } from 'hono/http-exception'

app.get('/users/:id', async (c) => {
  const user = await findUser(c.req.param('id'))

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

  return c.json(user)
})

// Global error handler
app.onError((err, c) => {
  console.error(`${err}`)

  if (err instanceof HTTPException) {
    return err.getResponse()
  }

  return c.json({ error: 'Internal Server Error' }, 500)
})

// Not found handler
app.notFound((c) => {
  return c.json({ error: 'Route not found' }, 404)
})
```

### Custom Error Classes

```typescript
class ValidationError extends HTTPException {
  constructor(errors: string[]) {
    super(400, {
      message: 'Validation failed',
      cause: errors
    })
  }
}

class AuthenticationError extends HTTPException {
  constructor() {
    super(401, { message: 'Authentication required' })
  }
}
```

## Runtime-Specific Exports

### Cloudflare Workers

```typescript
// src/index.ts
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare!'))

export default app
```

### Node.js

```typescript
// src/index.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Node!'))

serve({
  fetch: app.fetch,
  port: 3000
})
```

### Bun

```typescript
// src/index.ts
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Bun!'))

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

### Deno

```typescript
// main.ts
import { Hono } from 'npm:hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Deno!'))

Deno.serve(app.fetch)
```

## Best Practices

##

Related in General