docs: add Hono RPC + TanStack Query recipe#1159
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.
📝 WalkthroughWalkthroughThis pull request adds documentation for integrating Hono RPC with TanStack Query, including a new pattern guide covering server-side routing, type exports, client setup, validation, and query management, plus updates to the cookbook index and pattern metadata. Changes
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
Comment |
📦 Canary Packages Publishedversion: PackagesInstallAdd to your {
"dependencies": {
"@agentuity/evals": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-evals-1.0.41-1ad261d.tgz",
"@agentuity/drizzle": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-drizzle-1.0.41-1ad261d.tgz",
"@agentuity/react": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-react-1.0.41-1ad261d.tgz",
"@agentuity/cli": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-cli-1.0.41-1ad261d.tgz",
"@agentuity/postgres": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-postgres-1.0.41-1ad261d.tgz",
"@agentuity/runtime": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-runtime-1.0.41-1ad261d.tgz",
"@agentuity/core": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-core-1.0.41-1ad261d.tgz",
"@agentuity/opencode": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-opencode-1.0.41-1ad261d.tgz",
"@agentuity/schema": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-schema-1.0.41-1ad261d.tgz",
"@agentuity/claude-code": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-claude-code-1.0.41-1ad261d.tgz",
"@agentuity/workbench": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-workbench-1.0.41-1ad261d.tgz",
"@agentuity/frontend": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-frontend-1.0.41-1ad261d.tgz",
"@agentuity/server": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-server-1.0.41-1ad261d.tgz",
"@agentuity/coder": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-coder-1.0.41-1ad261d.tgz",
"@agentuity/auth": "https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-auth-1.0.41-1ad261d.tgz"
}
}Or install directly: bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-evals-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-drizzle-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-react-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-cli-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-postgres-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-runtime-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-core-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-opencode-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-schema-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-claude-code-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-workbench-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-frontend-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-server-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-coder-1.0.41-1ad261d.tgz
bun add https://agentuity-sdk-objects.t3.storageapi.dev/npm/1.0.41-1ad261d/agentuity-auth-1.0.41-1ad261d.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
- Convert to .mdx in apps/docs/src/web/content/cookbook/patterns/ - Add frontmatter with title and description - Add to patterns/meta.json navigation - Add CardLink entry to cookbook/index.mdx - Use action-oriented title - Replace blockquotes with <Callout> components (info/warning) - Replace⚠️ emoji section with <Callout type="warning"> - Use code block title="..." instead of inline comments for file paths - Add // [!code highlight] on lines that differ from previous sections
There was a problem hiding this comment.
🧹 Nitpick comments (3)
apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx (3)
308-323: Consider reducing duplication with the previous section.Lines 312-323 repeat the router composition pattern already shown in lines 280-291. Since this section's unique value is demonstrating
createApp({ router })and custom mount paths, you could reference the previous section's router definition instead of repeating it.Alternative approach
Replace the repeated router definition with a reference:
## With Explicit Routing -If you're using `createApp({ router })`, export the type from the shared types file: - -```typescript title="src/api/index.ts" -import { createRouter } from '@agentuity/runtime'; -import users from './users'; -import posts from './posts'; - -const router = createRouter() - .route('/users', users) - .route('/posts', posts); - -export type AppRoute = typeof router; -export default router; -``` +Using the composed router from the previous section, wire it up with `createApp()`: ```typescript title="src/app.ts"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx` around lines 308 - 323, The section duplicates the router composition shown earlier; remove the repeated router definition (the createRouter() composition and export type AppRoute) and instead reference or import the previously defined router, then show wiring with createApp({ router }) and custom mount paths (mentioning createApp, router, and AppRoute so reviewers can locate the original symbols); update the prose to say "Using the composed router from the previous section, wire it up with createApp()" and provide the minimal example showing createApp({ router }) rather than re-defining routes.
64-66: Consider usingwarningcallout type for the chaining requirement.This callout describes a common pitfall where type inference fails if methods aren't chained. Per coding guidelines,
warningis for "gotchas and required setup" whileinfois for "context and clarifications." Since failing to chain breaks type inference, this is more of a gotcha.Suggested change
-<Callout type="info" title="Chain Your Methods"> +<Callout type="warning" title="Chain Your Methods"> Chain `.get()`, `.post()`, etc. directly on `createRouter()`. If you use separate `router.get(...)` statements, TypeScript can't infer the combined type. </Callout>As per coding guidelines: "Use 'info' callout type for context and clarifications, 'warning' for gotchas and required setup".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx` around lines 64 - 66, Update the callout type from "info" to "warning" for the Chain Your Methods block because this is a gotcha that breaks TypeScript inference; locate the Callout element that wraps the text about chaining `.get()`/`.post()` on `createRouter()` and change its `type` prop to `warning` so the guidance aligns with the project's callout guidelines.
391-395: Clarify the distinction between compile-time and runtime validation.The leading comment "TypeScript error if you pass invalid data" doesn't clearly apply to the first example, which passes structurally valid strings that only Zod catches at runtime.
Suggested clarification
-// TypeScript error if you pass invalid data -createUser.mutate({ name: '', email: 'not-an-email' }); // runtime validation catches this -createUser.mutate({ wrong: 'field' }); // compile-time error +// Structural errors are caught at compile time, value errors at runtime +createUser.mutate({ name: '', email: 'not-an-email' }); // Zod catches at runtime +createUser.mutate({ wrong: 'field' }); // TypeScript catches at compile time🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx` around lines 391 - 395, The current comment "TypeScript error if you pass invalid data" is misleading for the first example; update the surrounding text/comments to clearly distinguish compile-time (TypeScript) vs runtime (Zod) validation: state that createUser.mutate({ name: '', email: 'not-an-email' }) is allowed by the TypeScript type but rejected at runtime by Zod, while createUser.mutate({ wrong: 'field' }) triggers a compile-time TypeScript error because the shape doesn't match the inferred input type; reference createUser.mutate and Zod in the explanation so readers know which check happens when.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx`:
- Around line 308-323: The section duplicates the router composition shown
earlier; remove the repeated router definition (the createRouter() composition
and export type AppRoute) and instead reference or import the previously defined
router, then show wiring with createApp({ router }) and custom mount paths
(mentioning createApp, router, and AppRoute so reviewers can locate the original
symbols); update the prose to say "Using the composed router from the previous
section, wire it up with createApp()" and provide the minimal example showing
createApp({ router }) rather than re-defining routes.
- Around line 64-66: Update the callout type from "info" to "warning" for the
Chain Your Methods block because this is a gotcha that breaks TypeScript
inference; locate the Callout element that wraps the text about chaining
`.get()`/`.post()` on `createRouter()` and change its `type` prop to `warning`
so the guidance aligns with the project's callout guidelines.
- Around line 391-395: The current comment "TypeScript error if you pass invalid
data" is misleading for the first example; update the surrounding text/comments
to clearly distinguish compile-time (TypeScript) vs runtime (Zod) validation:
state that createUser.mutate({ name: '', email: 'not-an-email' }) is allowed by
the TypeScript type but rejected at runtime by Zod, while createUser.mutate({
wrong: 'field' }) triggers a compile-time TypeScript error because the shape
doesn't match the inferred input type; reference createUser.mutate and Zod in
the explanation so readers know which check happens when.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 996f990a-30a9-4007-8d52-b8150dc0be76
📒 Files selected for processing (3)
apps/docs/src/web/content/cookbook/index.mdxapps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdxapps/docs/src/web/content/cookbook/patterns/meta.json
📜 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). (6)
- GitHub Check: Build
- GitHub Check: Queue CLI Tests
- GitHub Check: Sandbox CLI Tests
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Cloud Deployment Tests
- GitHub Check: Agentuity Deployment
🧰 Additional context used
📓 Path-based instructions (2)
apps/docs/src/web/content/**/*.mdx
📄 CodeRabbit inference engine (apps/docs/src/web/content/AGENTS.md)
apps/docs/src/web/content/**/*.mdx: Write 1-2 sentences of motivation before showing code (context-then-code principle)
Use 'ctx.logger' in server/agent code examples, not 'console.log'
Include imports at the top of standalone code examples to ensure they are copy-pasteable and runnable
Use inline comments to explain intent ('why'), not syntax ('what')
Do not use suppression comments like '//@ts-ignore', '// eslint-disable', or other code suppressions in documentation examples
Use action-oriented titles (e.g., 'Calling Other Agents' not 'Agent Communication') for documentation pages
Keep feature documentation provider-agnostic where possible; use current model names in code examples and verify they are up to date before publishing
Use 'info' callout type for context and clarifications, 'warning' for gotchas and required setup, 'tip' for optimizations and advanced patterns
Make cross-links context-specific: include why the reader would follow the link (e.g., 'See Streaming Responses for chunked output patterns')
Link to external tools on first mention only; do not re-link on the same page
Replace hollow adjectives (e.g., 'powerful', 'seamless', 'enterprise-grade') with specific benefits; use precise language like 'consistent API', 'type-safe', 'observable', 'faster'
Explicitly mark optional parameters as 'optional' in prose; readers should not need to parse type signatures
Use inline code references to show API shape, config values, or method signatures without imports; use standalone examples for concepts that require copy-pasteable code
Prefer 'Prefersfrom@agentuity/schemafor schemas; show schema-agnostic SDK examples using other StandardSchema libraries (Zod, ArkType, Valibot) across documentation
Use a balance of raw SDK providers and AI SDK providers (openai(),anthropic()) in code examples throughout documentation
Strip boilerplate from code examples; show only the feature being demonstrated
Files:
apps/docs/src/web/content/cookbook/index.mdxapps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx
apps/docs/**/*.json
📄 CodeRabbit inference engine (apps/docs/AGENTS.md)
Workspace dependencies must use workspace:* protocol for local package references (
@agentuity/runtime,@agentuity/react,@agentuity/schema,@agentuity/workbench,@agentuity/evals,@agentuity/cli)
Files:
apps/docs/src/web/content/cookbook/patterns/meta.json
🔇 Additional comments (5)
apps/docs/src/web/content/cookbook/index.mdx (1)
6-6: LGTM!The new
Linkimport and CardLink entry are well-structured. The description uses precise language ("Type-safe API calls") rather than hollow adjectives, and the icon choice semantically represents the server-client connection pattern.Also applies to: 48-53
apps/docs/src/web/content/cookbook/patterns/meta.json (1)
7-7: LGTM!The new entry is correctly added in alphabetical order and the slug matches the new documentation file.
apps/docs/src/web/content/cookbook/patterns/hono-rpc-tanstack-query.mdx (3)
1-25: Well-structured documentation.The frontmatter, introduction, and installation sections follow the context-then-code principle well. The specific benefits listed (type-safe, automatic caching, zero duplication) use precise language rather than hollow adjectives. The callouts use appropriate types for their purpose.
26-99: Server and type export sections are clear and practical.Good coverage of:
- Method chaining requirement for type inference
- The type-only boundary pattern with
export typeto prevent server code leakage- Inline comments explaining intent (e.g., "In a real app, query your database here")
The warning callout about never importing server files from client code is appropriately flagged as a gotcha.
101-244: TanStack Query integration examples are comprehensive and copy-pasteable.The hooks demonstrate common patterns well:
- Query key hierarchies for granular invalidation
- Error checking with
res.okbeforeres.json()- Mutation callbacks for cache invalidation
- TypeScript type inference comments (
^?) help readers understand what types they'll get
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