> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/honojs/hono/llms.txt
> Use this file to discover all available pages before exploring further.

# Handlers

> Learn about request handlers in Hono, including handler types, signatures, returning responses, and error handling.

## Overview

Handlers are functions that process HTTP requests and return responses. In Hono, every route is associated with one or more handler functions.

## Handler Types

Hono defines two main handler types:

### Handler

A standard request handler that returns a response:

From `src/types.ts:76-81`:

```typescript theme={null}
type Handler<
  E extends Env = any,
  P extends string = any,
  I extends Input = BlankInput,
  R extends HandlerResponse<any> = any,
> = (c: Context<E, P, I>, next: Next) => R
```

### MiddlewareHandler

A middleware-style handler that must call `next()` or return a response:

From `src/types.ts:83-88`:

```typescript theme={null}
type MiddlewareHandler<
  E extends Env = any,
  P extends string = string,
  I extends Input = {},
  R extends HandlerResponse<any> = Response,
> = (c: Context<E, P, I>, next: Next) => Promise<R | void>
```

### Combined Handler Type

The `H` type represents either a Handler or MiddlewareHandler:

From `src/types.ts:90-95`:

```typescript theme={null}
type H<
  E extends Env = any,
  P extends string = any,
  I extends Input = BlankInput,
  R extends HandlerResponse<any> = any,
> = Handler<E, P, I, R> | MiddlewareHandler<E, P, I, R>
```

## Handler Signature

All handlers receive two parameters:

1. **Context (`c`)**: Provides access to request/response data and utilities
2. **Next (`next`)**: Function to pass control to the next handler

```typescript theme={null}
import { Context } from 'hono'
import type { Next } from 'hono'

app.get('/example', async (c: Context, next: Next) => {
  // Handler logic
  return c.json({ message: 'Hello' })
})
```

## Handler Response Types

Handlers can return various response types:

From `src/types.ts:70-74`:

```typescript theme={null}
type HandlerResponse<O> =
  | Response
  | TypedResponse<O>
  | Promise<Response | TypedResponse<O>>
  | Promise<void>
```

### Returning Responses

#### Synchronous Response

```typescript theme={null}
app.get('/sync', (c) => {
  return c.text('Synchronous response')
})
```

#### Asynchronous Response

```typescript theme={null}
app.get('/async', async (c) => {
  const data = await fetchData()
  return c.json(data)
})
```

#### Raw Response Object

```typescript theme={null}
app.get('/raw', (c) => {
  return new Response('Raw response', {
    status: 200,
    headers: { 'Content-Type': 'text/plain' }
  })
})
```

## Basic Handler Examples

### Simple GET Handler

```typescript theme={null}
app.get('/hello', (c) => {
  return c.text('Hello, World!')
})
```

### POST Handler with JSON

```typescript theme={null}
app.post('/users', async (c) => {
  const body = await c.req.json()
  const user = { id: 1, ...body }
  return c.json(user, 201)
})
```

### Handler with Path Parameters

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

### Handler with Query Parameters

```typescript theme={null}
app.get('/search', (c) => {
  const query = c.req.query('q')
  const page = c.req.query('page') || '1'
  return c.json({ query, page })
})
```

## Multiple Handlers

Routes can have multiple handlers that execute in sequence:

```typescript theme={null}
const authenticate = async (c, next) => {
  const token = c.req.header('Authorization')
  if (!token) {
    return c.text('Unauthorized', 401)
  }
  c.set('authenticated', true)
  await next()
}

const logRequest = async (c, next) => {
  console.log(`${c.req.method} ${c.req.path}`)
  await next()
}

app.get('/protected',
  logRequest,
  authenticate,
  (c) => {
    return c.json({ message: 'Protected data' })
  }
)
```

## Type-Safe Handlers

Define environment and input types for type safety:

```typescript theme={null}
import { Hono } from 'hono'
import type { Context } from 'hono'

type Env = {
  Bindings: {
    DB: D1Database
  }
  Variables: {
    user: { id: string; name: string }
  }
}

const app = new Hono<Env>()

app.get('/users', async (c: Context<Env>) => {
  const db = c.env.DB // Type: D1Database
  const user = c.get('user') // Type: { id: string; name: string }
  
  const users = await db.prepare('SELECT * FROM users').all()
  return c.json(users)
})
```

## Error Handling

### Throwing Errors

Handlers can throw errors that are caught by the error handler:

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

app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  const user = await findUser(id)
  
  if (!user) {
    throw new HTTPException(404, { message: 'User not found' })
  }
  
  return c.json(user)
})

app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse()
  }
  return c.text('Internal Server Error', 500)
})
```

### Try-Catch in Handlers

```typescript theme={null}
app.post('/data', async (c) => {
  try {
    const body = await c.req.json()
    const result = await processData(body)
    return c.json(result)
  } catch (error) {
    console.error('Processing failed:', error)
    return c.json({ error: 'Failed to process data' }, 400)
  }
})
```

### Error Handler Types

From `src/types.ts:113-119`:

```typescript theme={null}
export interface HTTPResponseError extends Error {
  getResponse: () => Response
}

export type ErrorHandler<E extends Env = any> = (
  err: Error | HTTPResponseError,
  c: Context<E>
) => Response | Promise<Response>
```

## Async Handlers

All async operations should use `async/await`:

```typescript theme={null}
app.get('/async-data', async (c) => {
  // Parallel requests
  const [users, posts] = await Promise.all([
    fetchUsers(),
    fetchPosts()
  ])
  
  return c.json({ users, posts })
})

app.post('/upload', async (c) => {
  const formData = await c.req.formData()
  const file = formData.get('file')
  
  if (!file) {
    return c.text('No file uploaded', 400)
  }
  
  await saveFile(file)
  return c.json({ message: 'File uploaded' })
})
```

## Streaming Responses

Handlers can return streaming responses:

```typescript theme={null}
app.get('/stream', (c) => {
  const stream = new ReadableStream({
    async start(controller) {
      for (let i = 0; i < 10; i++) {
        await sleep(100)
        controller.enqueue(`chunk ${i}\n`)
      }
      controller.close()
    }
  })
  
  return c.body(stream)
})

app.get('/sse', (c) => {
  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder()
      for (let i = 0; i < 5; i++) {
        await sleep(1000)
        const data = `data: ${JSON.stringify({ count: i })}\n\n`
        controller.enqueue(encoder.encode(data))
      }
      controller.close()
    }
  })
  
  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
    }
  })
})
```

## Handler Registration

From `src/hono-base.ts:385-391`, handlers are added to routes:

```typescript theme={null}
#addRoute(method: string, path: string, handler: H): void {
  method = method.toUpperCase()
  path = mergePath(this._basePath, path)
  const r: RouterRoute = { basePath: this._basePath, path, method, handler }
  this.router.add(method, path, [handler, r])
  this.routes.push(r)
}
```

## Handler Execution

From `src/hono-base.ts:423-442`, handlers are executed:

```typescript theme={null}
// Do not `compose` if it has only one handler
if (matchResult[0].length === 1) {
  let res: ReturnType<H>
  try {
    res = matchResult[0][0][0][0](c, async () => {
      c.res = await this.#notFoundHandler(c)
    })
  } catch (err) {
    return this.#handleError(err, c)
  }

  return res instanceof Promise
    ? res
        .then(
          (resolved: Response | undefined) =>
            resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
        )
        .catch((err: Error) => this.#handleError(err, c))
    : (res ?? this.#notFoundHandler(c))
}
```

## Handler Context Passing

Pass data between handlers using context variables:

```typescript theme={null}
const setTimestamp = async (c, next) => {
  c.set('timestamp', Date.now())
  await next()
}

const logDuration = async (c, next) => {
  await next()
  const start = c.get('timestamp')
  const duration = Date.now() - start
  console.log(`Request took ${duration}ms`)
}

app.get('/timed',
  setTimestamp,
  logDuration,
  (c) => {
    return c.text('Done')
  }
)
```

## Not Found Handler

Customize the 404 handler:

```typescript theme={null}
app.notFound((c) => {
  return c.json(
    {
      message: 'Not Found',
      path: c.req.path
    },
    404
  )
})
```

From `src/types.ts:107-111`:

```typescript theme={null}
export type NotFoundHandler<E extends Env = any> = (
  c: Context<E>
) => NotFoundResponse extends Response
  ? NotFoundResponse | Promise<NotFoundResponse>
  : Response | Promise<Response>
```

## Custom Error Handler

Define a global error handler:

```typescript theme={null}
app.onError((err, c) => {
  console.error(`${err}`)
  
  if (err instanceof HTTPException) {
    const response = err.getResponse()
    return c.json(
      { error: err.message },
      response.status as any
    )
  }
  
  return c.json(
    { error: 'Internal Server Error' },
    500
  )
})
```

From `src/hono-base.ts:35-42`:

```typescript theme={null}
const errorHandler: ErrorHandler = (err, c) => {
  if ('getResponse' in err) {
    const res = err.getResponse()
    return c.newResponse(res.body, res)
  }
  console.error(err)
  return c.text('Internal Server Error', 500)
}
```

## Best Practices

<Tip>
  * Always return a response from handlers
  * Use `async/await` for asynchronous operations
  * Throw `HTTPException` for HTTP errors with status codes
  * Use TypeScript to define environment and variable types
  * Keep handlers focused on a single responsibility
  * Use middleware for cross-cutting concerns
</Tip>

<Warning>
  Handlers should always return a Response object. If a handler doesn't return anything, Hono will throw an error: "Context is not finalized. Did you forget to return a Response object?"
</Warning>

## Common Patterns

### CRUD Operations

```typescript theme={null}
// Create
app.post('/users', async (c) => {
  const user = await c.req.json()
  const created = await db.createUser(user)
  return c.json(created, 201)
})

// Read
app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  const user = await db.getUser(id)
  return user ? c.json(user) : c.notFound()
})

// Update
app.put('/users/:id', async (c) => {
  const id = c.req.param('id')
  const updates = await c.req.json()
  const updated = await db.updateUser(id, updates)
  return c.json(updated)
})

// Delete
app.delete('/users/:id', async (c) => {
  const id = c.req.param('id')
  await db.deleteUser(id)
  return c.body(null, 204)
})
```

### File Upload Handler

```typescript theme={null}
app.post('/upload', async (c) => {
  const formData = await c.req.formData()
  const file = formData.get('file') as File
  
  if (!file) {
    return c.json({ error: 'No file provided' }, 400)
  }
  
  const arrayBuffer = await file.arrayBuffer()
  const saved = await saveToStorage(arrayBuffer, file.name)
  
  return c.json({
    message: 'File uploaded',
    filename: file.name,
    size: file.size,
    url: saved.url
  })
})
```

### Pagination Handler

```typescript theme={null}
app.get('/items', async (c) => {
  const page = parseInt(c.req.query('page') || '1')
  const limit = parseInt(c.req.query('limit') || '10')
  const offset = (page - 1) * limit
  
  const [items, total] = await Promise.all([
    db.getItems(limit, offset),
    db.countItems()
  ])
  
  return c.json({
    items,
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit)
    }
  })
})
```

## Related

* [Routing](/concepts/routing) - Learn about route definition
* [Context](/concepts/context) - Understand the Context object
* [Middleware](/concepts/middleware) - Add middleware to handlers
* [Validation](/concepts/validation) - Validate request data
