A minimal example demonstrating how to use hono-rpc-query to integrate Hono RPC with React Query.
server/- Hono backend with REST API endpointsclient/- React frontend using React Query
From the root of the repository, build the library first:
pnpm install --frozen-lockfile
pnpm buildEach example has its own workspace boundary and lockfile, so installs stay independent of the library's Vite+ toolchain:
pnpm --dir example/server install --frozen-lockfile
pnpm --dir example/client install --frozen-lockfilecd example/server
pnpm devThe server will start on http://localhost:3001
In a new terminal:
cd example/client
pnpm devThe client will start on http://localhost:5173
From the repository root, after building the library and installing both examples:
pnpm --dir example/server typecheck
pnpm --dir example/client buildThese checks also run in CI. Use Node.js 24 LTS and the pnpm version pinned in package.json (Corepack selects it automatically).
The server defines a simple REST API with CRUD operations for posts:
GET /posts- List all postsGET /posts/:id- Get a single postPOST /posts- Create a new postDELETE /posts/:id- Delete a post
The route type is exported for the client to use:
export type AppRoutes = typeof routesThe client creates a Hono RPC client and wraps it with hcQuery:
import { hc } from 'hono/client'
import { hcQuery } from 'hono-rpc-query'
import type { AppRoutes } from '../../server/src/index'
const client = hc<AppRoutes>('http://localhost:3001')
export const api = hcQuery(client)The api object provides queryOptions and mutationOptions for all endpoints:
// Queries - pass options directly to queryOptions
const postsQuery = useQuery(api.posts.$get.queryOptions({}))
// Mutations - pass options directly to mutationOptions
const createMutation = useMutation(
api.posts.$post.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: api.posts.$get.queryOptions({}).queryKey,
})
},
})
)
// With path parameters
const deleteMutation = useMutation(
api.posts[':id'].$delete.mutationOptions({
onSuccess: () => {
// Handle success
},
})
)
deleteMutation.mutate({ param: { id: '1' } })