Call your API routes with full type safety using Hono's built-in RPC client. The type comes directly from your exported router, so there is no separate route registry to maintain.
Setup
1. Export your router type from your API file:
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import type { Env } from '@agentuity/runtime';
const router = new Hono<Env>()
.post('/chat', zValidator('json', z.object({ message: z.string() })), async (c) => {
const { message } = c.req.valid('json');
return c.json({ response: `Echo: ${message}` });
})
.get('/users', async (c) => {
return c.json([{ id: '1', name: 'Alice' }]);
});
export default router;
// Export the type for use with hc()
export type ApiRouter = typeof router;2. Create the client in your frontend:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const sameOriginClient = hc<ApiRouter>('/api');
const remoteClient = hc<ApiRouter>('https://your-project.agentuity.cloud/api');Basic Usage
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const client = hc<ApiRouter>('/api');
// POST with JSON body — fully typed from the zValidator schema
const res = await client.chat.$post({ json: { message: 'Hello' } });
const data = await res.json();
console.log(data.response);
// GET request
const usersRes = await client.users.$get();
const users = await usersRes.json();For React components, manage loading and error state yourself with useState, or use the pattern from React Hooks.
HTTP Methods
Hono's RPC client uses $get, $post, $put, $delete, and $patch to match your route definitions:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const client = hc<ApiRouter>('/api');
const users = await (await client.users.$get()).json();
const created = await (await client.users.$post({ json: { name: 'Alice', email: 'alice@example.com' } })).json();
await client.users[':id'].$put({ param: { id: 'alice' }, json: { name: 'Alice Smith' } });
await client.users[':id'].$delete({ param: { id: 'alice' } });WebSocket Connections
Use $ws() for WebSocket routes:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const client = hc<ApiRouter>('/api');
const ws = client.chat.$ws();
ws.addEventListener('open', () => {
console.log('Connected');
ws.send(JSON.stringify({ message: 'Hello!' }));
});
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
});
ws.addEventListener('error', (event) => {
console.error('WebSocket error:', event);
});
ws.close();Server-Sent Events
For SSE routes, use the standard EventSource API since Hono's RPC client does not wrap SSE:
const events = new EventSource('/api/notifications');
events.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Event:', data);
});
events.addEventListener('error', () => {
console.error('Stream error');
});
// Close when done
events.close();If your SSE endpoint needs authentication, prefer same-origin cookies or a signed URL. The browser EventSource API does not let you attach arbitrary request headers.
Client Options
Pass a custom fetch or base URL when creating the client:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
// With auth headers via a custom fetch wrapper
const client = hc<ApiRouter>('/api', {
fetch: (input, init) =>
fetch(input, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${getToken()}`,
},
}),
});Request Cancellation
Pass an AbortSignal through the fetch option:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const controller = new AbortController();
const client = hc<ApiRouter>('/api', {
fetch: (input, init) =>
fetch(input, { ...init, signal: controller.signal }),
});
const resultPromise = client.longProcess.$post({ json: { data: 'large payload' } });
// Cancel if needed
setTimeout(() => controller.abort(), 5000);
try {
const result = await resultPromise;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
console.log('Request cancelled');
}
}Error Handling
The client returns Response objects — check the status yourself:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';
const client = hc<ApiRouter>('/api');
const res = await client.users.$post({ json: { name: 'Alice' } });
if (!res.ok) {
const error = await res.json();
if (res.status >= 400 && res.status < 500) {
console.error('Client error:', res.status, error);
} else {
console.error('Server error:', res.status, error);
}
} else {
const user = await res.json();
console.log('Created:', user);
}Type Safety
The type for your router is derived directly from your route definitions — no code generation needed. Export the type from your API file and import it in your frontend:
import { zValidator } from '@hono/zod-validator';
import { Hono } from 'hono';
import { z } from 'zod';
const chatSchema = z.object({
message: z.string(),
});
const router = new Hono()
.post('/chat', zValidator('json', chatSchema), async (c) => {
const { message } = c.req.valid('json');
return c.json({ response: `Echo: ${message}` });
});
export type ApiRouter = typeof router; // Captures all route typesThe client infers input and output types from the router, so $post({ json: ... }) is fully typed without RPCRouteRegistry, generated route files, or a separate client schema.
Use Hono's zValidator or vValidator middleware to get input type inference on the client side. Without a validator, request body types default to unknown.
RPC Client vs React Hooks
| Use Case | Recommended |
|---|---|
| React components | hc() plus useState or your preferred data library |
| Non-React apps (Vue, Svelte, vanilla JS) | hc() from hono/client |
| Server-side JavaScript | hc() from hono/client |
| CLI tools or scripts | fetch directly |
Calling Routes from External Backends
External backends like Next.js or Express can use HTTP to call Agentuity routes that access storage services. See SDK Utilities for External Apps for the complete pattern.
Next Steps
- React Hooks: State-managed hooks for React apps
- Provider Setup:
AgentuityProviderconfiguration for@agentuity/reactapps - Adding Authentication: Protect routes with Agentuity Auth
- WebSockets: Create WebSocket routes
- Server-Sent Events: Create SSE routes