Skip to main content

What is the RPC Client?

The Hono RPC (Remote Procedure Call) client provides a type-safe way to make HTTP requests to your Hono server. Using the hc() function, you get full TypeScript type inference for routes, request parameters, and response data - all derived directly from your server definitions.
import { hc } from 'hono/client'

const client = hc<typeof app>('http://localhost:8787')

Key Benefits

Type Safety

Full TypeScript support with type inference from server routes. Catch errors at compile time.

Zero Config

No code generation or build steps. Types are inferred directly from your Hono app.

Auto-complete

Get intelligent suggestions for routes, parameters, and response properties in your IDE.

Multi-runtime

Works anywhere JavaScript runs - browsers, Node.js, Deno, Bun, and edge runtimes.

Basic Setup

1. Define Your Server

First, create a Hono app with some routes:
import { Hono } from 'hono'

const app = new Hono()
  .get('/hello', (c) => c.json({ message: 'Hello!' }))
  .post('/posts', (c) => c.json({ id: 123, title: 'New Post' }, 201))

export type AppType = typeof app
Export the app type so it can be imported by your client code.

2. Create a Client

Import the app type and create a typed client:
import { hc } from 'hono/client'
import type { AppType } from './server'

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

// Make requests with full type safety
const res = await client.hello.$get()
const data = await res.json()
console.log(data.message) // TypeScript knows this is a string

How It Works

The RPC client uses TypeScript’s powerful type inference to extract route information from your Hono app. When you call hc<typeof app>(), TypeScript analyzes your app’s route definitions and generates the appropriate client methods.
// Server side
const app = new Hono()
  .get('/api/users/:id', (c) => {
    return c.json({ id: 123, name: 'John' })
  })

// Client side - TypeScript infers everything
const client = hc<typeof app>('http://localhost')
const res = await client.api.users[':id'].$get({
  param: { id: '123' } // TypeScript knows this param is required
})
const user = await res.json()
// TypeScript knows user has { id: number, name: string }

Request Methods

The client generates methods for each HTTP verb, prefixed with $:
  • $get() - GET requests
  • $post() - POST requests
  • $put() - PUT requests
  • $patch() - PATCH requests
  • $delete() - DELETE requests
  • $options() - OPTIONS requests
const app = new Hono()
  .get('/posts', (c) => c.json({ posts: [] }))
  .post('/posts', (c) => c.json({ id: 1 }, 201))
  .delete('/posts/:id', (c) => c.json({ success: true }))

const client = hc<typeof app>('http://localhost')

// All methods are typed
await client.posts.$get()
await client.posts.$post()
await client.posts[':id'].$delete({ param: { id: '1' } })

Path Parameters

Path parameters use bracket notation in the client:
// Server
const app = new Hono()
  .get('/posts/:id/comments/:commentId', (c) => {
    return c.json({ postId: 1, commentId: 2 })
  })

// Client
const client = hc<typeof app>('http://localhost')
const res = await client.posts[':id'].comments[':commentId'].$get({
  param: {
    id: '123',
    commentId: '456'
  }
})

Configuration Options

You can pass configuration options when creating the client:
const client = hc<AppType>('http://localhost', {
  // Custom headers (static or dynamic)
  headers: {
    'Authorization': 'Bearer token',
    'X-Custom': 'value'
  },
  
  // Or use a function for dynamic headers
  headers: () => ({
    'Authorization': `Bearer ${getToken()}`
  }),
  
  // Custom fetch implementation
  fetch: customFetch,
  
  // Custom RequestInit options
  init: {
    credentials: 'include'
  }
})
The init option has the highest priority and can overwrite settings that Hono sets for you, including body, method, and headers. Use with caution.

URL and Path Helpers

The client provides utility methods to construct URLs and paths:
// Get the full URL
const url = client.posts[':id'].$url({ 
  param: { id: '123' },
  query: { format: 'json' }
})
console.log(url.href) // http://localhost/posts/123?format=json

// Get just the path
const path = client.posts[':id'].$path({ 
  param: { id: '123' },
  query: { format: 'json' }
})
console.log(path) // /posts/123?format=json

What’s Next?

Type Safety

Learn about type inference and InferResponseType

Usage Examples

Explore request patterns and error handling

Build docs developers (and LLMs) love