Conversation
End-to-end type-safe API calls from React to Agentuity routes: - Server: chained createRouter() for type inference - Client: hc() typed client with TanStack Query hooks - Safe type sharing via src/shared/api-types.ts (export type only) - Multiple route files, composed routers, explicit routing - Zod validation with types flowing to client Includes a dedicated section on safely exporting types to prevent server code (database clients, secrets, env vars) from leaking into the client bundle through bundler module tracing.
📝 WalkthroughWalkthroughA new documentation file has been added describing the integration between Hono RPC and TanStack Query. The guide covers server-side route definition, client-side setup, type inference patterns, Zod validation, and best practices for maintaining type safety across the full stack. Changes
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
📦 Canary Packages Publishedversion: PackagesInstallAdd to your {
"dependencies": {
"@agentuity/frontend": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-frontend-1.0.39-d2557a8.tgz",
"@agentuity/postgres": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-postgres-1.0.39-d2557a8.tgz",
"@agentuity/drizzle": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-drizzle-1.0.39-d2557a8.tgz",
"@agentuity/evals": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-evals-1.0.39-d2557a8.tgz",
"@agentuity/runtime": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-runtime-1.0.39-d2557a8.tgz",
"@agentuity/server": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-server-1.0.39-d2557a8.tgz",
"@agentuity/workbench": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-workbench-1.0.39-d2557a8.tgz",
"@agentuity/opencode": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-opencode-1.0.39-d2557a8.tgz",
"@agentuity/react": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-react-1.0.39-d2557a8.tgz",
"@agentuity/core": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-core-1.0.39-d2557a8.tgz",
"@agentuity/auth": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-auth-1.0.39-d2557a8.tgz",
"@agentuity/claude-code": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-claude-code-1.0.39-d2557a8.tgz",
"@agentuity/coder": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-coder-1.0.39-d2557a8.tgz",
"@agentuity/cli": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-cli-1.0.39-d2557a8.tgz",
"@agentuity/schema": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-schema-1.0.39-d2557a8.tgz"
}
}Or install directly: bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-frontend-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-postgres-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-drizzle-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-evals-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-runtime-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-server-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-workbench-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-opencode-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-react-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-core-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-auth-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-claude-code-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-coder-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-cli-1.0.39-d2557a8.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.39-d2557a8/agentuity-schema-1.0.39-d2557a8.tgz |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
docs/recipes/hono-rpc-tanstack-query.md (3)
21-58: Consider clarifying server-side router mounting earlier.The server example defines routes at
/users,/users/:id, etc., but doesn't show how this router is mounted on the server. The client examples usehc<UsersRoute>('/api'), implying the router is mounted at/api. While this is explained later (line 345: "Default mount is /api"), readers following the first example might be confused about the complete setup.📝 Suggested addition to clarify mounting
Consider adding a brief note or code snippet after line 58:
export type UsersRoute = typeof router; export default router; + +// Mount this router in your app: +// export const app = await createApp({ router }); +// By default, the router is mounted at /api🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/recipes/hono-rpc-tanstack-query.md` around lines 21 - 58, Add a short clarification after the createRouter() example that shows where the router is mounted and matches the client usage: explain that the exported router (router / UsersRoute) should be mounted on your server under the same base path the client uses (e.g., "/api"), reference the createRouter() export (export default router and export type UsersRoute) and the client call hc<UsersRoute>('/api'), and instruct to mount the router on that path so the example routes (/users, /users/:id) become /api/users and /api/users/:id.
156-170: Consider mentioning mutation error handling.The mutation examples throw errors but don't show how to handle them in the component. While TanStack Query's
useMutationreturns anerrorproperty that can be used in the UI, theUserListcomponent example (lines 196-239) doesn't demonstrate error handling for mutations.💡 Example of mutation error handling
const createUser = useCreateUser(); // In the component: {createUser.error && ( <div>Error creating user: {createUser.error.message}</div> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/recipes/hono-rpc-tanstack-query.md` around lines 156 - 170, Update the docs to demonstrate handling mutation errors from useCreateUser in the UI: in the UserList component example, consume the return value from useCreateUser (the mutation object from useMutation) and render its error state (e.g., show createUser.error.message) so users see how to surface mutation errors; reference the useCreateUser hook and its useMutation return (and keep the existing onSuccess behavior that calls queryClient.invalidateQueries(['users'])) when describing the fix.
363-403: Consider adding Zod validation error handling guidance.The section shows how to set up Zod validation but doesn't demonstrate how validation errors are returned to the client or how to handle them. When validation fails, Hono's
zValidatortypically returns a 400 response with error details, but the example query functions always throw generic errors on!res.ok.📖 Example of handling validation errors
mutationFn: async (data: { name: string; email: string }) => { const res = await client.users.$post({ json: data }); if (!res.ok) { const error = await res.json(); // Zod validation errors include details about which fields failed throw new Error(error.message || 'Failed to create user'); } return res.json(); }This helps users understand how to provide better error feedback to end-users when validation fails.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/recipes/hono-rpc-tanstack-query.md` around lines 363 - 403, Add guidance showing how to handle Zod validation errors from Hono by reading the response body when the client call is not ok and surface field-level details; update the docs around the createUser example to demonstrate checking res.ok after client.users.$post (or inside mutationFn), calling await res.json() to extract the validation error payload returned by zValidator, and then throwing or mapping error.message/error.details back to the UI (e.g., returning form field errors) instead of throwing a generic error on !res.ok.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@docs/recipes/hono-rpc-tanstack-query.md`:
- Around line 21-58: Add a short clarification after the createRouter() example
that shows where the router is mounted and matches the client usage: explain
that the exported router (router / UsersRoute) should be mounted on your server
under the same base path the client uses (e.g., "/api"), reference the
createRouter() export (export default router and export type UsersRoute) and the
client call hc<UsersRoute>('/api'), and instruct to mount the router on that
path so the example routes (/users, /users/:id) become /api/users and
/api/users/:id.
- Around line 156-170: Update the docs to demonstrate handling mutation errors
from useCreateUser in the UI: in the UserList component example, consume the
return value from useCreateUser (the mutation object from useMutation) and
render its error state (e.g., show createUser.error.message) so users see how to
surface mutation errors; reference the useCreateUser hook and its useMutation
return (and keep the existing onSuccess behavior that calls
queryClient.invalidateQueries(['users'])) when describing the fix.
- Around line 363-403: Add guidance showing how to handle Zod validation errors
from Hono by reading the response body when the client call is not ok and
surface field-level details; update the docs around the createUser example to
demonstrate checking res.ok after client.users.$post (or inside mutationFn),
calling await res.json() to extract the validation error payload returned by
zValidator, and then throwing or mapping error.message/error.details back to the
UI (e.g., returning form field errors) instead of throwing a generic error on
!res.ok.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 93f0370c-7bc1-49c6-8354-ba8147ef4550
📒 Files selected for processing (1)
docs/recipes/hono-rpc-tanstack-query.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Storage CLI Tests
- GitHub Check: Queue CLI Tests
- GitHub Check: Standalone Agent Test
- GitHub Check: Pack & Upload
- GitHub Check: Framework Integration Tests (TanStack & Next.js)
- GitHub Check: Package Installation & Usage Test
- GitHub Check: Postgres SSL Integration Test
- GitHub Check: Build
- GitHub Check: Sandbox CLI Tests
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Template Integration Tests
- GitHub Check: Cloud Deployment Tests
- GitHub Check: Playwright E2E Smoke Test
- GitHub Check: Queue SDK Tests
| @@ -0,0 +1,411 @@ | |||
| # Hono RPC with TanStack Query | |||
|
|
|||
There was a problem hiding this comment.
This should be converted to an .mdx file within apps/docs/src/web/content/ (suggestion: apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx).
Some more notes:
- Add frontmatter (title, description) matching the existing pattern pages
- Add to
cookbook/patterns/meta.jsonso it shows up in the nav - Add a
<CardLink>entry tocookbook/index.mdxin the Patterns section (lucide icon)
|
|
||
| ## Safely Exporting Types to the Client | ||
|
|
||
| ⚠️ **Never import server route files directly from client code.** Even `import type` can cause bundlers to trace the module graph and pull server-only code (database clients, secrets, etc.) into the client bundle. |
There was a problem hiding this comment.
Suggestion: replace <Callout> component (<Callout type="warning" title="...">; available in .mdx pages).
| bun add @tanstack/react-query hono | ||
| ``` | ||
|
|
||
| > `hono` is already a dependency of `@agentuity/runtime`, but you need it in your app for the `hc` client import. |
There was a problem hiding this comment.
Suggestion: replace blockquote with <Callout type="info"> (same for lines 60 and 91).
| @@ -0,0 +1,411 @@ | |||
| # Hono RPC with TanStack Query | |||
There was a problem hiding this comment.
Suggestion: use an action-oriented title (e.g., "Type-Safe API Calls with Hono RPC and TanStack Query").
| Use method chaining on `createRouter()` so TypeScript can infer the full route tree: | ||
|
|
||
| ```typescript | ||
| // src/api/users.ts |
There was a problem hiding this comment.
Use code block title="src/api/users.ts" instead of inline comments for file paths. (can be applied to all code blocks here)
| // client.posts.$get() — typed | ||
| ``` | ||
|
|
||
| ## With Explicit Routing |
There was a problem hiding this comment.
Consider using // [!code highlight] on the lines that differ from the previous section's code blocks.
Summary
New recipe at
docs/recipes/hono-rpc-tanstack-query.mdcovering end-to-end type-safe API calls from React to Agentuity routes using Hono RPC and TanStack Query.Contents
createRouter()for full type inferencesrc/shared/api-types.tswithexport typeto prevent server code leaking into client bundlesuseQuery,useMutation, query invalidation patternscreateApp({ router })and custom mount paths@hono/zod-validatorwith types flowing through to the clientWhy the safe type sharing section
Even with
import type, bundlers like Vite can trace the module graph and pull server-only imports (database clients,process.env,@agentuity/runtimeinternals) into the client bundle. The recipe recommends asrc/shared/api-types.tsboundary file that uses onlyexport type { ... }statements, which TypeScript erases completely at compile time.Summary by CodeRabbit