Skip to main content

Overview

The Hono client provides several utility types for extracting request and response types from your API endpoints, enabling full type safety in your client code.

Import

import type {
  InferRequestType,
  InferResponseType,
  ClientRequestOptions,
  ClientResponse,
  Fetch
} from 'hono/client'

Type Inference Utilities

InferRequestType

Extracts the request type (arguments) from a client endpoint method. Type Signature:
type InferRequestType<T> = T extends (
  args: infer R,
  options: any | undefined
) => Promise<ClientResponse<unknown>>
  ? NonNullable<R>
  : never
Usage:
import { hc } from 'hono/client'
import type { AppType } from './server'
import type { InferRequestType } from 'hono/client'

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

// Extract request type for a specific endpoint
type PostsCreateRequest = InferRequestType<typeof client.api.posts.$post>
// Result: { json: { title: string; content: string } }

// Use in function signatures
function createPost(data: PostsCreateRequest) {
  return client.api.posts.$post(data)
}
Example with validation:
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

// Server route with validation
const route = app.post(
  '/posts',
  zValidator('json', z.object({
    title: z.string(),
    content: z.string(),
    tags: z.array(z.string()).optional()
  })),
  (c) => {
    const data = c.req.valid('json')
    return c.json({ id: '123', ...data })
  }
)

// Client type inference
type CreatePostInput = InferRequestType<typeof client.api.posts.$post>
// Result: { json: { title: string; content: string; tags?: string[] } }

InferResponseType

Extracts the response type from a client endpoint method, with optional status code filtering. Type Signature:
type InferResponseType<T, StatusCode extends number = StatusCode> = // ...
T
endpoint method
required
The client endpoint method to infer from
StatusCode
number
default:"any status code"
Optional status code to filter by. Only returns the response type for matching status codes.
Usage:
import type { InferResponseType } from 'hono/client'

// Infer response type for any status code
type PostsResponse = InferResponseType<typeof client.api.posts.$get>
// Result: { id: string; title: string; content: string }[]

// Infer response type for specific status code
type PostsSuccess = InferResponseType<typeof client.api.posts.$get, 200>
// Result: { id: string; title: string; content: string }[]

type PostsNotFound = InferResponseType<typeof client.api.posts[':id'].$get, 404>
// Result: { error: string }
Example with multiple response types:
// Server route with different responses
app.get('/posts/:id', (c) => {
  const id = c.req.param('id')
  const post = findPost(id)
  
  if (!post) {
    return c.json({ error: 'Not found' }, 404)
  }
  
  return c.json({ id, title: post.title }, 200)
})

// Client type inference
type SuccessResponse = InferResponseType<typeof client.api.posts[':id'].$get, 200>
// Result: { id: string; title: string }

type ErrorResponse = InferResponseType<typeof client.api.posts[':id'].$get, 404>
// Result: { error: string }

type AnyResponse = InferResponseType<typeof client.api.posts[':id'].$get>
// Result: { id: string; title: string } | { error: string }

Client Configuration Types

ClientRequestOptions

Configuration options for the RPC client, used both at client creation and per-request. Type Definition:
type ClientRequestOptions<T = unknown> = {
  fetch?: typeof fetch | HonoRequest
  webSocket?: (...args: ConstructorParameters<typeof WebSocket>) => WebSocket
  init?: RequestInit
  buildSearchParams?: BuildSearchParamsFn
} & (keyof T extends never
  ? {
      headers?:
        | Record<string, string>
        | (() => Record<string, string> | Promise<Record<string, string>>)
    }
  : {
      headers: T | (() => T | Promise<T>)
    })
fetch
typeof fetch
Custom fetch implementation. Useful for testing with MSW or using node-fetch.
headers
Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)
Headers to include with requests. Can be static object or async function for dynamic headers.
init
RequestInit
Standard RequestInit options. Takes highest priority and can override Hono’s automatic settings.
buildSearchParams
BuildSearchParamsFn
Custom function to serialize query parameters.
webSocket
(...args: ConstructorParameters<typeof WebSocket>) => WebSocket
Custom WebSocket constructor.
Usage:
import { hc } from 'hono/client'
import type { ClientRequestOptions } from 'hono/client'

const options: ClientRequestOptions = {
  headers: async () => {
    const token = await getAuthToken()
    return { 'Authorization': `Bearer ${token}` }
  },
  init: {
    credentials: 'include'
  }
}

const client = hc('http://localhost:3000', options)

BuildSearchParamsFn

Function type for custom query parameter serialization. Type Definition:
type BuildSearchParamsFn = (
  query: Record<string, string | string[]>
) => URLSearchParams
Usage:
import qs from 'qs'

const buildSearchParams: BuildSearchParamsFn = (query) => {
  // Use bracket notation for arrays: key[]=a&key[]=b
  return new URLSearchParams(qs.stringify(query))
}

const client = hc('http://localhost:3000', {
  buildSearchParams
})

Response Types

ClientResponse

Type-safe response interface that extends the standard Response with inferred types. Type Definition:
interface ClientResponse<
  T,
  U extends number = StatusCode,
  F extends ResponseFormat = ResponseFormat
> {
  readonly body: ReadableStream | null
  readonly bodyUsed: boolean
  ok: U extends SuccessStatusCode
    ? true
    : U extends Exclude<StatusCode, SuccessStatusCode>
      ? false
      : boolean
  redirected: boolean
  status: U
  statusText: string
  type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect'
  headers: Headers
  url: string
  redirect(url: string, status: number): Response
  clone(): Response
  bytes(): Promise<Uint8Array<ArrayBuffer>>
  json(): Promise<T>
  text(): Promise<string>
  blob(): Promise<Blob>
  formData(): Promise<FormData>
  arrayBuffer(): Promise<ArrayBuffer>
}
T
generic
The response body type (inferred from server route)
U
number
The HTTP status code
F
ResponseFormat
The response format (‘json’, ‘text’, etc.)
Key Features:
  • The ok property type is narrowed based on status code
  • The json() method returns Promise<T> with the inferred response type
  • The text() method returns Promise<string> (or Promise<T> for text responses)
Usage:
const response = await client.api.posts.$get()

// Type-safe property access
if (response.ok) {
  // TypeScript knows this is a success response
  const data = await response.json() // Type is inferred
}

// Check status
if (response.status === 404) {
  const error = await response.json() // Error type
}

FilterClientResponseByStatusCode

Filters a ClientResponse type to only include specific status codes. Type Definition:
type FilterClientResponseByStatusCode<
  T extends ClientResponse<any, any, any>,
  U extends number = StatusCode
> = T extends ClientResponse<infer RT, infer RC, infer RF>
  ? RC extends U
    ? ClientResponse<RT, RC, RF>
    : never
  : never
Usage:
import type { FilterClientResponseByStatusCode } from 'hono/client'

type AllResponses = ClientResponse<Data, 200 | 404, 'json'>

type SuccessOnly = FilterClientResponseByStatusCode<AllResponses, 200>
// Result: ClientResponse<Data, 200, 'json'>

type ErrorsOnly = FilterClientResponseByStatusCode<AllResponses, 404>
// Result: ClientResponse<Data, 404, 'json'>

Fetch Type

A helper type for creating functions that wrap client endpoints. Type Definition:
type Fetch<T> = (
  args?: InferRequestType<T>,
  opt?: ClientRequestOptions
) => Promise<ClientResponseOfEndpoint<InferEndpointType<T>>>
Usage:
import type { Fetch } from 'hono/client'

// Create a typed wrapper function
function createPostWrapper(client: ReturnType<typeof hc<AppType>>) {
  const endpoint = client.api.posts.$post
  
  const createPost: Fetch<typeof endpoint> = async (args, options) => {
    // Add custom logic before/after
    console.log('Creating post...', args)
    const response = await endpoint(args, options)
    console.log('Response:', response.status)
    return response
  }
  
  return createPost
}

Advanced Types

ApplyGlobalResponse

Applies global response definitions to your Hono app type. Type Definition:
type ApplyGlobalResponse<App, Def extends GlobalResponseDefinition> = // ...

type GlobalResponseDefinition = {
  [S in StatusCode]?: {
    [F in KnownResponseFormat]?: unknown
  }
}
Usage:
import type { ApplyGlobalResponse } from 'hono/client'

// Define global error responses
type GlobalErrors = {
  404: { json: { error: 'Not found' } }
  500: { json: { error: 'Internal server error' } }
}

type AppWithErrors = ApplyGlobalResponse<typeof app, GlobalErrors>

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

Complete Example

Here’s a complete example using all the main client types:
import { hc } from 'hono/client'
import type {
  InferRequestType,
  InferResponseType,
  ClientRequestOptions
} from 'hono/client'
import type { AppType } from './server'

// Configure client with options
const options: ClientRequestOptions = {
  headers: async () => ({
    'Authorization': `Bearer ${await getToken()}`
  })
}

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

// Extract types
type CreatePostInput = InferRequestType<typeof client.api.posts.$post>
type CreatePostOutput = InferResponseType<typeof client.api.posts.$post, 201>
type CreatePostError = InferResponseType<typeof client.api.posts.$post, 400>

// Create type-safe wrapper functions
async function createPost(input: CreatePostInput): Promise<CreatePostOutput> {
  const response = await client.api.posts.$post(input)
  
  if (!response.ok) {
    throw new Error('Failed to create post')
  }
  
  return response.json()
}

// Use the function
const newPost = await createPost({
  json: {
    title: 'Hello Hono',
    content: 'This is type-safe!'
  }
})

console.log(newPost.id, newPost.title) // Fully typed!

Build docs developers (and LLMs) love