Skip to content

Context

To handle Request and Response, you can use Context object.

req

req is the instance of HonoRequest.

ts
app.get('/hello', (c) => {
  const userAgent = c.req.header('User-Agent')
  ...
})

body()

Return the HTTP response.

You can set headers with c.header() and set HTTP status code with c.status. This can also be set in c.text(), c.json() and so on.

INFO

Note: When returning Text or HTML, it is recommended to use c.text() or c.html().

ts
app.get('/welcome', (c) => {
  // Set headers
  c.header('X-Message', 'Hello!')
  c.header('Content-Type', 'text/plain')

  // Set HTTP status code
  c.status(201)

  // Return the response body
  return c.body('Thank you for coming')
})

You can also write the following.

ts
app.get('/welcome', (c) => {
  return c.body('Thank you for coming', 201, {
    'X-Message': 'Hello!',
    'Content-Type': 'text/plain',
  })
})

The Response is the same as below.

ts
new Response('Thank you for coming', {
  status: 201,
  headers: {
    'X-Message': 'Hello!',
    'Content-Type': 'text/plain',
  },
})

text()

Render text as Content-Type:text/plain.

ts
app.get('/say', (c) => {
  return c.text('Hello!')
})

json()

Render JSON as Content-Type:application/json.

ts
app.get('/api', (c) => {
  return c.json({ message: 'Hello!' })
})

html()

Render HTML as Content-Type:text/html.

ts
app.get('/', (c) => {
  return c.html('<h1>Hello! Hono!</h1>')
})

notFound()

Return the Not Found Response.

ts
app.get('/notfound', (c) => {
  return c.notFound()
})

redirect()

Redirect, default status code is 302.

ts
app.get('/redirect', (c) => {
  return c.redirect('/')
})
app.get('/redirect-permanently', (c) => {
  return c.redirect('/', 301)
})

res

ts
// Response object
app.use('/', async (c, next) => {
  await next()
  c.res.headers.append('X-Debug', 'Debug message')
})

set() / get()

Set the value specified by the key with set and use it later with get.

ts
app.use(async (c, next) => {
  c.set('message', 'Hono is cool!!')
  await next()
})

app.get('/', (c) => {
  const message = c.get('message')
  return c.text(`The message is "${message}"`)
})

Pass the Variables as Generics to the constructor of Hono to make it type-safe.

ts
type Variables = {
  message: string
}

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

var

You can also access the value of a variable with c.var.

ts
const result = c.var.client.oneMethod()

If you want to create the middleware which provides a custom method, write like the following:

ts
type Env = {
  Variables: {
    echo: (str: string) => string
  }
}

const app = new Hono()

const echoMiddleware = createMiddleware<Env>(async (c, next) => {
  c.set('echo', (str) => str)
  await next()
})

app.get('/echo', echoMiddleware, (c) => {
  return c.text(c.var.echo('Hello!'))
})

If you want to use the middleware in multiple handlers, you can use app.use(). Then, you have to pass the Env as Generics to the constructor of Hono to make it type-safe.

ts
const app = new Hono<Env>()

app.use(echoMiddleware)

app.get('/echo', (c) => {
  return c.text(c.var.echo('Hello!'))
})

render() / setRenderer()

You can set a layout using c.setRenderer() within a custom middleware.

tsx
app.use(async (c, next) => {
  c.setRenderer((content) => {
    return c.html(
      <html>
        <body>
          <p>{content}</p>
        </body>
      </html>
    )
  })
  await next()
})

Then, you can utilize c.render() to create responses within this layout.

ts
app.get('/', (c) => {
  return c.render('Hello!')
})

The output of which will be:

html
<html>
  <body>
    <p>Hello!</p>
  </body>
</html>

Additionally, this feature offers the flexibility to customize arguments. To ensure type safety, types can be defined as:

ts
declare module 'hono' {
  interface ContextRenderer {
    (
      content: string | Promise<string>,
      head: { title: string }
    ): Response | Promise<Response>
  }
}

Here's an example of how you can use this:

ts
app.use('/pages/*', async (c, next) => {
  c.setRenderer((content, head) => {
    return c.html(
      <html>
        <head>
          <title>{head.title}</title>
        </head>
        <body>
          <header>{head.title}</header>
          <p>{content}</p>
        </body>
      </html>
    )
  })
  await next()
})

app.get('/pages/my-favorite', (c) => {
  return c.render(<p>Ramen and Sushi</p>, {
    title: 'My favorite',
  })
})

app.get('/pages/my-hobbies', (c) => {
  return c.render(<p>Watching baseball</p>, {
    title: 'My hobbies',
  })
})

executionCtx

ts
// ExecutionContext object
app.get('/foo', async (c) => {
  c.executionCtx.waitUntil(
    c.env.KV.put(key, data)
  )
  ...
})

event

ts
// Type definition to make type inference
type Bindings = {
  MY_KV: KVNamespace
}

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


// FetchEvent object (only set when using Service Worker syntax)
app.get('/foo', async (c) => {
  c.event.waitUntil(
    c.env.MY_KV.put(key, data)
  )
  ...
})

env

In Cloudflare Workers Environment variables, secrets, KV namespaces, D1 database, R2 bucket etc. that are bound to a worker are known as bindings. Regardless of type, bindings are always available as global variables and can be accessed via the context c.env.BINDING_KEY.

ts
// Type definition to make type inference
type Bindings = {
  MY_KV: KVNamespace
}

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

// Environment object for Cloudflare Workers
app.get('/', (c) => {
  c.env.MY_KV.get('my-key')
  // ...
})

error

If the Handler throws an error, the error object is placed in c.error. You can access it in your middleware.

ts
app.use(async (c, next) => {
  await next()
  if (c.error) {
    // do something...
  }
})

ContextVariableMap

For instance, if you wish to add type definitions to variables when a specific middleware is used, you can extend ContextVariableMap. For example:

ts
declare module 'hono' {
  interface ContextVariableMap {
    result: string
  }
}

You can then utilize this in your middleware:

ts
const mw = createMiddleware(async (c, next) => {
  c.set('result', 'some values') // result is a string
  await next()
})

In a handler, the variable is inferred as the proper type:

ts
app.get('/', (c) => {
  const val = c.get('result') // val is a string
  //...
})

Released under the MIT License.