---
title: "RPC Client"
description: "The Hono RPC Client is a type-safe interface generator that enables seamless communication between a frontend or client-side application and a Hono server. By leveraging TypeScript's inference capa..."
last_updated: "2026-07-02T09:13:47.263646+00:00"
canonical_url: "https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/developer-helpers/rpc-client"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/types.ts](https://github.com/blade47/hono/blob/main/src/types.ts)
- [src/client/client.ts](https://github.com/blade47/hono/blob/main/src/client/client.ts)
- [src/request.ts](https://github.com/blade47/hono/blob/main/src/request.ts)
- [src/client/types.ts](https://github.com/blade47/hono/blob/main/src/client/types.ts)
- [package.json](https://github.com/blade47/hono/blob/main/package.json)
- [src/hono.ts](https://github.com/blade47/hono/blob/main/src/hono.ts)
- [src/hono-base.ts](https://github.com/blade47/hono/blob/main/src/hono-base.ts)
</details>

The Hono RPC Client is a type-safe interface generator that enables seamless communication between a frontend or client-side application and a Hono server. By leveraging TypeScript's inference capabilities, it maps the server's routing structure to a proxy object, effectively allowing developers to call server-side routes as if they were local functions.

The core philosophy of the RPC client is to eliminate the need for manual endpoint synchronization. Instead of writing `fetch` calls with string-based URLs, developers use the proxy interface generated by `hc` (Hono Client). This ensures that request parameters, body types, and response formats are strictly typed, reducing runtime errors and improving developer ergonomics.

Architecturally, the RPC client uses a JavaScript `Proxy` to intercept property access and method calls. This allows it to dynamically construct the request path based on the chain of property accesses (e.g., `client.users.posts[":id"].$get()`). The client then translates these calls into standard HTTP requests, providing a consistent API surface regardless of the underlying server-side complexity.

## Proxy Interception Mechanism

The RPC client relies on the `createProxy` function to build a chainable API surface. Every time a property is accessed on the client object, `createProxy` is called recursively, appending the key to a `path` array. This continues until a method is invoked, which triggers the `apply` trap of the proxy.

The mechanism uses a `Callback` function that captures the `path` and the `args` passed during the invocation. This callback acts as the entry point for determining how to handle the request — whether it's a standard HTTP fetch, a URL calculation, or a WebSocket connection.

```mermaid
flowchart LR
    A["Proxy (Client)"] -- .users.posts --> B["Proxy (Path: ['users', 'posts'])"]
    B -- .[":id"] --> C["Proxy (Path: ['users', 'posts', ':id'])"]
    C -- .$get() --> D["Callback (Path, Args)"]
```
Sources: [src/client/client.ts:15-31](https://github.com/blade47/hono/blob/main/src/client/client.ts#L15-L31)

## Request Execution Flow

When a user calls a method (e.g., `$get`), the callback creates an instance of `ClientRequestImpl`. This class is responsible for the actual network interaction. It transforms the provided `args` (including `json`, `query`, or `form`) into a standard `Request` object and executes the fetch request.

The request flow follows this chain:
1.  **Path Reconstruction:** The client joins the accumulated path array and merges it with the `baseUrl`.
2.  **Parameter Injection:** `replaceUrlParam` substitutes placeholders like `:id` with actual values from the arguments.
3.  **Search Params:** If query parameters are provided, they are converted to `URLSearchParams` and appended to the URL.
4.  **Body Serialization:** Depending on the input target (e.g., `args.json`), the body is stringified and the `Content-Type` header is set to `application/json`.

Sources: [src/client/client.ts:33-130](https://github.com/blade47/hono/blob/main/src/client/client.ts#L33-L130)

## WebSocket Integration

The RPC client handles WebSocket connections via the `$ws` endpoint. When invoked, it translates the path into a WebSocket URL (replacing `http` with `ws` protocol). It optionally serializes query parameters into the URL search string.

The `establishWebSocket` helper allows for custom WebSocket implementations, defaulting to the native browser `WebSocket`. The client ensures that any provided path parameters are replaced before the connection is established.

Sources: [src/client/client.ts:187-212](https://github.com/blade47/hono/blob/main/src/client/client.ts#L187-L212)

## Configuration and Options

The `ClientRequestOptions` type provides granular control over how the RPC client communicates with the server. It allows users to define custom `fetch` functions (useful for testing or proxying) and headers that are added to every request.

| Option | Type | Purpose |
| :--- | :--- | :--- |
| `fetch` | `typeof fetch` | Custom fetch function to execute requests. |
| `headers` | `Record | function` | Headers to include in the request. |
| `init` | `RequestInit` | Standard `RequestInit` configuration. |
| `buildSearchParams` | `BuildSearchParamsFn` | Strategy for query parameter serialization. |

Sources: [src/client/types.ts:32-65](https://github.com/blade47/hono/blob/main/src/client/types.ts#L32-L65)

> [!NOTE]
> When providing custom `init` options, exercise caution: these take the highest priority and can overwrite critical Hono-managed properties like `method`, `body`, or `headers`.

## Practical Usage Example

The following example demonstrates how to initialize the RPC client and use it to call an API endpoint.

```typescript
import { hc } from 'hono/client'
import type { AppType } from './server' // Imported from the server

const client = hc<AppType>('https://api.example.com')

// Calling an endpoint: GET /users/:id
const res = await client.users[':id'].$get({
  param: { id: '123' },
  query: { details: 'true' }
})

if (res.ok) {
  const data = await res.json()
  console.log(data)
}
```
Sources: [src/client/client.ts:133-224](https://github.com/blade47/hono/blob/main/src/client/client.ts#L133-L224)

## Design Trade-offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| `Proxy` object | Allows clean, natural JS/TS syntax for API calls. | Can hide complexity, making debugging through chains difficult. |
| Type inference via `hc` | Zero manual API schema management. | Increases reliance on build-time TypeScript compilation. |
| `ClientRequestImpl` isolation | Encapsulates logic for path/body construction. | Creates a secondary layer between the proxy callback and native `fetch`. |

Sources: [src/client/client.ts](https://github.com/blade47/hono/blob/main/src/client/client.ts)

## Related

- [Schema Validation](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/developer-helpers/schema-validation)


## Sitemap

See the full [sitemap](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
