Skip to main content

Context

The Context object is passed to every route handler and middleware function. It provides access to the request, response helpers, environment bindings, and context variables.

Constructor

You typically don’t create Context instances directly. Hono creates them for each request.
const c = new Context(request, options?)

Request Properties

req

The HonoRequest instance for accessing request data.
c.req: HonoRequest<P, I['out']>
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  const query = c.req.query('search')
  return c.json({ id, query })
})
See the HonoRequest API reference for available methods.

env

Environment bindings (e.g., Cloudflare Workers KV, D1, etc.).
c.env: E['Bindings']
app.get('/todos', async (c) => {
  // Access Cloudflare D1 database
  const todos = await c.env.DB.prepare('SELECT * FROM todos').all()
  return c.json(todos)
})

event

The FetchEvent associated with the current request (Cloudflare Workers).
c.event: FetchEventLike
Throws an error if the context does not have a FetchEvent.
app.get('/data', async (c) => {
  // Use waitUntil for async operations that shouldn't block response
  c.event.waitUntil(
    fetch('https://analytics.example.com/track')
  )
  return c.text('OK')
})

executionCtx

The ExecutionContext for the current request.
c.executionCtx: ExecutionContext
Throws an error if the context does not have an ExecutionContext.
app.get('/data', async (c) => {
  c.executionCtx.waitUntil(analyticsTask())
  return c.text('Processing')
})

error

Error object if a handler threw an error (available in error handlers).
c.error: Error | undefined
app.use('*', async (c, next) => {
  await next()
  if (c.error) {
    console.error('Request error:', c.error)
  }
})

finalized

Boolean indicating whether the response has been finalized.
c.finalized: boolean

res

The Response object for the current request.
c.res: Response
app.use('*', async (c, next) => {
  await next()
  console.log('Status:', c.res.status)
})

Response Methods

text

Respond with plain text.
c.text(text, status?, headers?)
c.text(text, init?)
text
string
required
Text content to send
status
StatusCode
HTTP status code (default: 200)
headers
HeaderRecord
Additional headers to set
return
Response
Response with Content-Type: text/plain
app.get('/hello', (c) => {
  return c.text('Hello, World!')
})

app.get('/error', (c) => {
  return c.text('Not Found', 404)
})

json

Respond with JSON.
c.json(object, status?, headers?)
c.json(object, init?)
object
JSONValue | {}
required
Object to serialize as JSON
status
StatusCode
HTTP status code (default: 200)
headers
HeaderRecord
Additional headers to set
return
Response
Response with Content-Type: application/json
app.get('/api/user', (c) => {
  return c.json({ name: 'John', age: 30 })
})

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

html

Respond with HTML.
c.html(html, status?, headers?)
c.html(html, init?)
html
string | Promise<string>
required
HTML content to send
status
StatusCode
HTTP status code (default: 200)
headers
HeaderRecord
Additional headers to set
return
Response | Promise<Response>
Response with Content-Type: text/html
app.get('/', (c) => {
  return c.html('<h1>Hello, World!</h1>')
})

app.get('/page', (c) => {
  return c.html(
    <html>
      <body>
        <h1>Welcome</h1>
      </body>
    </html>
  )
})

body

Respond with a raw body.
c.body(data, status?, headers?)
c.body(data, init?)
data
Data | null
required
Body data (string, ArrayBuffer, ReadableStream, or Uint8Array)
status
StatusCode
HTTP status code
headers
HeaderRecord
Headers to set
return
Response
Response with the provided body
app.get('/binary', (c) => {
  const buffer = new Uint8Array([1, 2, 3, 4])
  return c.body(buffer)
})

redirect

Redirect to a different URL.
c.redirect(location, status?)
location
string | URL
required
URL to redirect to
status
RedirectStatusCode
HTTP redirect status code (default: 302)
return
Response
Redirect response with Location header
app.get('/old-path', (c) => {
  return c.redirect('/new-path')
})

app.get('/permanent', (c) => {
  return c.redirect('/new-url', 301)
})

notFound

Return a 404 Not Found response.
c.notFound()
return
Response
404 Not Found response
app.get('/users/:id', async (c) => {
  const user = await findUser(c.req.param('id'))
  if (!user) {
    return c.notFound()
  }
  return c.json(user)
})

newResponse

Create a new Response with merged headers.
c.newResponse(data, status?, headers?)
c.newResponse(data, init?)
data
Data | null
required
Response body
status
StatusCode
HTTP status code
headers
HeaderRecord
Headers to set
return
Response
New Response with headers merged from c.header() calls
app.get('/data', (c) => {
  c.header('X-Custom', 'value')
  return c.newResponse('Hello', 200)
})

Header Methods

Set a response header.
c.header(name, value?, options?)
name
string
required
Header name
value
string
Header value. If undefined, the header is deleted.
options
SetHeadersOptions
Options object with append boolean
app.get('/data', (c) => {
  c.header('X-Custom', 'value')
  c.header('Content-Type', 'application/json')
  return c.body(JSON.stringify({ data: 'value' }))
})

// Append header
c.header('Set-Cookie', 'token=abc', { append: true })
c.header('Set-Cookie', 'user=john', { append: true })

status

Set the response status code.
c.status(status)
status
StatusCode
required
HTTP status code to set
app.get('/data', (c) => {
  c.status(201)
  return c.json({ created: true })
})
It’s usually cleaner to pass the status directly to response methods like c.json(data, 201) instead of calling c.status() separately.

Context Variables

Context variables allow you to pass data between middleware and handlers.

set

Set a context variable.
c.set(key, value)
key
string
required
Variable name
value
any
required
Value to store
app.use('*', async (c, next) => {
  c.set('requestTime', Date.now())
  c.set('user', { id: 1, name: 'John' })
  await next()
})

get

Get a context variable.
c.get(key)
key
string
required
Variable name
return
any
The stored value, or undefined if not set
app.get('/', (c) => {
  const user = c.get('user')
  const time = c.get('requestTime')
  return c.json({ user, time })
})

var

Read-only object containing all context variables.
c.var: Readonly<ContextVariableMap & E['Variables']>
app.get('/', (c) => {
  const { user, requestTime } = c.var
  return c.json({ user, requestTime })
})
c.var provides type-safe access to variables when using TypeScript with proper type definitions.

Rendering Methods

render

Render content using the configured renderer.
c.render(...args)
return
Response | Promise<Response>
Rendered response
app.get('/', (c) => {
  return c.render('Hello!')
})

setRenderer

Set a custom renderer for the application.
c.setRenderer(renderer)
renderer
Renderer
required
Function that renders content to a Response
app.use('*', async (c, next) => {
  c.setRenderer((content) => {
    return c.html(
      <html>
        <body>
          <div>{content}</div>
        </body>
      </html>
    )
  })
  await next()
})

app.get('/', (c) => c.render('Hello!'))
// Renders: <html><body><div>Hello!</div></body></html>

setLayout

Set a layout component.
c.setLayout(layout)
layout
Layout
required
Layout component function
return
Layout
The layout function

getLayout

Get the current layout.
c.getLayout()
return
Layout | undefined
The current layout function, or undefined

Type Parameters

The Context class accepts generic type parameters for type safety:
E
Env
default:"any"
Environment type defining bindings and variables
P
string
default:"any"
Path parameter type for type-safe param access
I
Input
default:"{}"
Input type for validated data
type Env = {
  Bindings: {
    DB: D1Database
  }
  Variables: {
    user: User
  }
}

app.get('/users/:id', (c: Context<Env, '/users/:id'>) => {
  const id = c.req.param('id') // Type-safe
  const db = c.env.DB // Type-safe
  const user = c.var.user // Type-safe
  return c.json({ id })
})

Build docs developers (and LLMs) love