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> = // ...
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>)
})
Custom fetch implementation. Useful for testing with MSW or using node-fetch.
Headers to include with requests. Can be static object or async function for dynamic headers.
Standard RequestInit options. Takes highest priority and can override Hono’s automatic settings.
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>
}
The response body type (inferred from server route)
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!