Skip to main content

Overview

Hono is built with TypeScript and provides excellent type safety out of the box. With Hono’s type system, you get full autocomplete and type checking for routes, middleware, validation, and more.

Environment Types

Define types for your application’s environment variables and context variables using the Env type.

Bindings

Bindings are environment-specific values (like environment variables on Cloudflare Workers):
import { Hono } from 'hono'

type Bindings = {
  TOKEN: string
  KV: KVNamespace
  DB: D1Database
}

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

app.get('/', (c) => {
  const token = c.env.TOKEN // typed as string
  const kv = c.env.KV // typed as KVNamespace
  return c.text('Hello!')
})

Variables

Variables are values you set and get using c.set() and c.get():
type Variables = {
  user: {
    id: string
    name: string
    role: 'admin' | 'user'
  }
  requestId: string
}

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

app.use('*', async (c, next) => {
  c.set('requestId', crypto.randomUUID())
  await next()
})

app.get('/profile', (c) => {
  const user = c.get('user') // fully typed!
  const requestId = c.get('requestId') // typed as string
  return c.json({ user, requestId })
})

Combined Environment

type Env = {
  Bindings: {
    DATABASE_URL: string
    API_KEY: string
  }
  Variables: {
    user: User
    session: Session
  }
}

const app = new Hono<Env>()

Type-Safe Routing

Hono infers types from your route patterns:
app.get('/posts/:id', (c) => {
  const id = c.req.param('id') // typed as string
  return c.json({ id })
})

app.get('/search/:category/:tag', (c) => {
  const { category, tag } = c.req.param()
  // both typed as string
  return c.json({ category, tag })
})

Validation Types

Get full type safety with validation:
import { validator } from 'hono/validator'

app.post(
  '/users',
  validator('json', (value, c) => {
    const parsed = value as { 
      name: string
      email: string 
      age: number 
    }
    // validation logic
    if (!parsed.name) {
      return c.text('Invalid!', 400)
    }
    return parsed
  }),
  (c) => {
    const data = c.req.valid('json')
    // data is typed as { name: string, email: string, age: number }
    return c.json({ created: true })
  }
)

Using Zod for Validation

Zod provides even better type inference:
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().min(18),
  role: z.enum(['admin', 'user']).default('user'),
})

type User = z.infer<typeof userSchema>

app.post(
  '/users',
  zValidator('json', userSchema),
  (c) => {
    const user = c.req.valid('json')
    // user is fully typed with all Zod transformations!
    return c.json({ user })
  }
)

Generic Types

Understand Hono’s key generic types:

Env

The environment type containing Bindings and Variables:
export type Env = {
  Bindings?: Bindings
  Variables?: Variables
}

Handler

The handler function type:
export 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

The middleware function type:
export 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>

Custom Middleware Types

Create type-safe custom middleware:
import { MiddlewareHandler } from 'hono/types'

type MyEnv = {
  Variables: {
    userId: string
  }
}

const authMiddleware: MiddlewareHandler<MyEnv> = async (c, next) => {
  const token = c.req.header('Authorization')
  
  if (!token) {
    return c.text('Unauthorized', 401)
  }
  
  const userId = await verifyToken(token)
  c.set('userId', userId) // type-checked!
  
  await next()
}

app.use('*', authMiddleware)

app.get('/me', (c) => {
  const userId = c.get('userId') // typed as string
  return c.json({ userId })
})

Response Types

Type your JSON responses:
type Post = {
  id: string
  title: string
  body: string
  createdAt: Date
}

app.get('/posts/:id', async (c) => {
  const post = await db.getPost(c.req.param('id'))
  return c.json<Post>(post) // Response typed as Post
})

Context Types

Import and use Context types in your code:
import { Context } from 'hono'

type Env = {
  Variables: {
    user: User
  }
}

async function getCurrentUser(c: Context<Env>): Promise<User> {
  return c.get('user')
}

app.get('/profile', async (c) => {
  const user = await getCurrentUser(c)
  return c.json(user)
})

Type Utilities

Hono provides type utilities for common patterns:
import type { 
  Input,
  BlankInput,
  BlankSchema,
  BlankEnv,
} from 'hono/types'

Module Augmentation

Extend Hono’s types using module augmentation:
declare module 'hono' {
  interface ContextVariableMap {
    user: User
    requestId: string
  }
}

// Now these are available globally
app.get('/any-route', (c) => {
  const user = c.get('user') // typed!
  const requestId = c.get('requestId') // typed!
  return c.json({ user, requestId })
})

RPC Type Safety

Get end-to-end type safety with Hono’s RPC client:
// server.ts
import { Hono } from 'hono'

const app = new Hono()
  .get('/posts/:id', (c) => {
    const id = c.req.param('id')
    return c.json({ id, title: 'Post Title' })
  })
  .post('/posts', async (c) => {
    const body = await c.req.json<{ title: string }>()
    return c.json({ id: '123', ...body }, 201)
  })

export type AppType = typeof app

// client.ts
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('http://localhost:3000')

const res = await client.posts[':id'].$get({
  param: { id: '123' }
})
const data = await res.json() // fully typed!

Best Practices

Always use type to define your environment generics, not interface.
Define your environment types once when creating the Hono instance.
Let TypeScript infer types where possible instead of explicit annotations.
Use Zod, Valibot, or similar libraries that provide excellent type inference.
Export your app type to enable type-safe RPC clients.

Build docs developers (and LLMs) love