Skip to content

Context

Request와 Response를 처리하기 위해 Context 객체를 사용할 수 있다.

req

req는 HonoRequest의 인스턴스이다. 자세한 내용은 HonoRequest를 참조한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/hello', (c) => {
  const userAgent = c.req.header('User-Agent')
  // ...
  // ---cut-start---
  return c.text(`Hello, ${userAgent}`)
  // ---cut-end---
})

body()

HTTP 응답을 반환한다.

c.header()로 헤더를 설정하고 c.status로 HTTP 상태 코드를 지정할 수 있다. 이는 c.text(), c.json() 등에서도 설정 가능하다.

INFO

참고: 텍스트나 HTML을 반환할 때는 c.text()c.html()을 사용하는 것이 좋다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/welcome', (c) => {
  // 헤더 설정
  c.header('X-Message', 'Hello!')
  c.header('Content-Type', 'text/plain')

  // HTTP 상태 코드 설정
  c.status(201)

  // 응답 본문 반환
  return c.body('Thank you for coming')
})

다음과 같이 작성할 수도 있다.

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

위 코드는 아래와 동일한 응답을 생성한다.

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

text()

Content-Type:text/plain으로 텍스트를 렌더링한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/say', (c) => {
  return c.text('Hello!')
})

json()

JSON을 Content-Type:application/json으로 렌더링한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/api', (c) => {
  return c.json({ message: 'Hello!' })
})

html()

Content-Type:text/html로 HTML을 렌더링한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
  return c.html('<h1>Hello! Hono!</h1>')
})

notFound()

Not Found 응답을 반환한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/notfound', (c) => {
  return c.notFound()
})

redirect()

redirect()는 기본 상태 코드로 302를 사용한다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/redirect', (c) => {
  return c.redirect('/')
})
app.get('/redirect-permanently', (c) => {
  return c.redirect('/', 301)
})

res

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// Response 객체
app.use('/', async (c, next) => {
  await next()
  c.res.headers.append('X-Debug', 'Debug message')
})

set() / get()

현재 요청 동안 임의의 키-값 쌍을 설정하고 가져올 수 있다. 이를 통해 미들웨어 간 또는 미들웨어에서 라우트 핸들러로 특정 값을 전달할 수 있다.

ts
import { Hono } from 'hono'
const app = new Hono<{ Variables: { message: string } }>()
// ---cut---
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}"`)
})

타입 안전성을 보장하려면 Hono 생성자에 Variables를 제네릭으로 전달한다.

ts
import { Hono } from 'hono'
// ---cut---
type Variables = {
  message: string
}

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

c.set / c.get의 값은 동일한 요청 내에서만 유지된다. 다른 요청 간에 공유하거나 유지할 수 없다.

변수

c.var를 사용해 변수의 값에 접근할 수 있다.

ts
import type { Context } from 'hono'
declare const c: Context
// ---cut---
const result = c.var.client.oneMethod()

커스텀 메서드를 제공하는 미들웨어를 만들려면 다음과 같이 작성한다.

ts
import { Hono } from 'hono'
import { createMiddleware } from 'hono/factory'
// ---cut---
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!'))
})

여러 핸들러에서 미들웨어를 사용하려면 app.use()를 활용한다. 이때 타입 안전성을 보장하려면 Hono 생성자에 Env를 제네릭으로 전달해야 한다.

ts
import { Hono } from 'hono'
import type { MiddlewareHandler } from 'hono/types'
declare const echoMiddleware: MiddlewareHandler
type Env = {
  Variables: {
    echo: (str: string) => string
  }
}
// ---cut---
const app = new Hono<Env>()

app.use(echoMiddleware)

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

render() / setRenderer()

커스텀 미들웨어 내에서 c.setRenderer()를 사용해 레이아웃을 설정할 수 있다.

tsx
/** @jsx jsx */
/** @jsxImportSource hono/jsx */
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async (c, next) => {
  c.setRenderer((content) => {
    return c.html(
      <html>
        <body>
          <p>{content}</p>
        </body>
      </html>
    )
  })
  await next()
})

그런 다음, 이 레이아웃 내에서 응답을 생성하기 위해 c.render()를 활용할 수 있다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
  return c.render('Hello!')
})

이 코드의 출력 결과는 다음과 같다:

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

또한, 이 기능은 인자를 커스텀할 수 있는 유연성을 제공한다. 타입 안전성을 보장하기 위해 다음과 같이 타입을 정의할 수 있다:

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

이를 활용한 예제는 다음과 같다:

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',
  })
})

실행 컨텍스트

ts
import { Hono } from 'hono'
const app = new Hono<{
  Bindings: {
    KV: any
  }
}>()
declare const key: string
declare const data: string
// ---cut---
// ExecutionContext 객체
app.get('/foo', async (c) => {
  c.executionCtx.waitUntil(c.env.KV.put(key, data))
  // ...
})

이벤트

ts
import { Hono } from 'hono'
declare const key: string
declare const data: string
type KVNamespace = any
// ---cut---
// 타입 추론을 위한 타입 정의
type Bindings = {
  MY_KV: KVNamespace
}

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

// FetchEvent 객체 (Service Worker 구문 사용 시에만 설정됨)
app.get('/foo', async (c) => {
  c.event.waitUntil(c.env.MY_KV.put(key, data))
  // ...
})

환경 변수

Cloudflare Workers에서 환경 변수, 시크릿, KV 네임스페이스, D1 데이터베이스, R2 버킷 등 워커에 바인딩된 요소를 바인딩(bindings)이라고 한다. 타입에 상관없이 바인딩은 항상 전역 변수로 사용할 수 있으며, c.env.BINDING_KEY를 통해 접근할 수 있다.

ts
import { Hono } from 'hono'
type KVNamespace = any
// ---cut---
// 타입 추론을 위한 타입 정의
type Bindings = {
  MY_KV: KVNamespace
}

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

// Cloudflare Workers의 환경 객체
app.get('/', async (c) => {
  c.env.MY_KV.get('my-key')
  // ...
})

오류 처리

핸들러에서 오류가 발생하면, 해당 오류 객체는 c.error에 저장된다. 미들웨어에서 이 오류 객체에 접근할 수 있다.

ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async (c, next) => {
  await next()
  if (c.error) {
    // 오류 처리 로직...
  }
})

ContextVariableMap

특정 미들웨어를 사용할 때 변수에 타입 정의를 추가하려면 ContextVariableMap을 확장하면 된다. 예를 들어:

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

이를 미들웨어에서 활용할 수 있다:

ts
import { createMiddleware } from 'hono/factory'
// ---cut---
const mw = createMiddleware(async (c, next) => {
  c.set('result', 'some values') // result는 string 타입
  await next()
})

핸들러에서는 변수가 적절한 타입으로 추론된다:

ts
import { Hono } from 'hono'
const app = new Hono<{ Variables: { result: string } }>()
// ---cut---
app.get('/', (c) => {
  const val = c.get('result') // val은 string 타입
  // ...
  return c.json({ result: val })
})

Released under the MIT License.