https://github.com/k3dom/hono-rpc-query
Integration for Hono's RPC client with React Query
https://github.com/k3dom/hono-rpc-query
hono react-query rpc
Last synced: about 2 months ago
JSON representation
Integration for Hono's RPC client with React Query
- Host: GitHub
- URL: https://github.com/k3dom/hono-rpc-query
- Owner: k3dom
- License: mit
- Created: 2025-05-08T14:52:00.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2026-02-01T19:31:55.000Z (about 2 months ago)
- Last Synced: 2026-02-01T20:46:46.170Z (about 2 months ago)
- Topics: hono, react-query, rpc
- Language: TypeScript
- Homepage:
- Size: 323 KB
- Stars: 29
- Watchers: 1
- Forks: 1
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# 🔥 hono-rpc-query
Eliminates boilerplate when integrating Hono's RPC client with TanStack React Query by automatically generating type-safe `queryOptions` and `mutationOptions` for all your endpoints.
## Installation
```bash
pnpm add hono-rpc-query
# or
npm install hono-rpc-query
# or
yarn add hono-rpc-query
```
## Quick Start
### 1. Set up your Hono server
```typescript
// server/index.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
const routes = app
.get('/posts', (c) => {
return c.json([
{ id: 1, title: 'Hello World' },
{ id: 2, title: 'Learning Hono' },
])
})
.get(
'/posts/:id',
zValidator('param', z.object({ id: z.coerce.number() })),
(c) => {
const { id } = c.req.valid('param')
// ... fetch post logic
return c.json({ id, title: 'Post title' })
}
)
.post(
'/posts',
zValidator('json', z.object({ title: z.string(), content: z.string() })),
(c) => {
const data = c.req.valid('json')
// ... create post logic
return c.json({ id: 3, ...data })
}
)
export type AppRoutes = typeof routes
```
### 2. Create the client wrapper
```typescript
// client/api.ts
import { hc } from 'hono/client'
import { hcQuery } from 'hono-rpc-query'
import type { AppRoutes } from '../server'
const client = hc('http://localhost:3001')
export const api = hcQuery(client)
```
### 3. Use in your React components
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { api } from './api'
function App() {
const queryClient = useQueryClient()
// Fetch all posts - note the empty object {} is required even with no input
const postsQuery = useQuery(api.posts.$get.queryOptions({}))
// Create post mutation - pass options directly to mutationOptions()
const createPost = useMutation(
api.posts.$post.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: api.posts.$get.queryOptions({}).queryKey,
})
},
})
)
const handleCreate = () => {
createPost.mutate({ json: { title: 'New Post', content: 'Content' } })
}
return
{/* ... */}
}
```
## API Reference
### `queryOptions(config)`
Generates TanStack Query options for GET requests. Returns an object with `queryKey` and `queryFn`.
**Important:** You must always pass an object to `queryOptions()`, even when there are no input arguments. Pass an empty object `{}` if no input is needed.
#### Usage with no input
```typescript
// Endpoint with no parameters
const options = api.posts.$get.queryOptions({})
// Returns: { queryKey: [...], queryFn: ... }
useQuery(options)
```
#### Usage with input parameters
```typescript
// Endpoint with path parameters
const options = api.posts[':id'].$get.queryOptions({
input: {
param: { id: '1' },
},
})
useQuery(options)
```
#### Usage with json parameters
```typescript
// Endpoint with query string
const options = api.posts.$get.queryOptions({
input: {
json: { page: 1, limit: 10 },
},
})
useQuery(options)
```
#### Additional TanStack Query options
You can pass any TanStack Query options alongside the input:
```typescript
const options = api.posts.$get.queryOptions({
input: { param: { id: 1 } },
enabled: true,
staleTime: 5000,
refetchOnWindowFocus: false,
})
useQuery(options)
```
### `mutationOptions(config)`
Generates TanStack Query options for POST, PUT, DELETE requests. Returns an object with `mutationKey` and `mutationFn`.
**Important:** You must always pass an object to `mutationOptions()`, even when configuring no additional options. Pass an empty object `{}` if no config is needed.
#### Basic usage
```typescript
// Always pass an object, even if empty
const mutation = useMutation(api.posts.$post.mutationOptions({}))
// Call the mutation with input
mutation.mutate({ json: { title: 'New Post', content: 'Content' } })
```
#### Usage with callbacks
Pass additional mutation options directly to `mutationOptions()`:
```typescript
const mutation = useMutation(
api.posts.$post.mutationOptions({
onSuccess: (data) => {
console.log('Created:', data)
},
onError: (error) => {
console.error('Failed:', error)
},
})
)
```
#### DELETE request example
```typescript
const deleteMutation = useMutation(
api.posts[':id'].$delete.mutationOptions({
onSuccess: () => {
// Invalidate queries after deletion
queryClient.invalidateQueries({
queryKey: api.posts.$get.queryOptions({}).queryKey,
})
},
})
)
// Call with parameters
deleteMutation.mutate({ param: { id: '1' } })
```
### Accessing Query Keys
You can access the generated query key for cache invalidation or other purposes:
```typescript
// Get the query key
const queryKey = api.posts.$get.queryOptions({}).queryKey
// Use it for invalidation
queryClient.invalidateQueries({ queryKey })
// Use it for setting query data
queryClient.setQueryData(queryKey, newData)
// Use it for getting cached data
const cachedData = queryClient.getQueryData(queryKey)
```
#### Query keys with parameters
```typescript
// Query key includes the input parameters
const queryKey = api.posts[':id'].$get.queryOptions({
input: { param: { id: 1 } },
}).queryKey
// Invalidate a specific post
queryClient.invalidateQueries({ queryKey })
// Invalidate all posts (partial matching)
queryClient.invalidateQueries({
queryKey: ['posts'], // Matches all posts-related queries
})
```
## Complete Example
Check out the [example](./example) directory for a full working implementation
## Known Limitations
- No support for `infiniteQueryOptions` (yet)
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
## License
[MIT](https://choosealicense.com/licenses/mit/)