npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@softnetics/hono-react-query

v1.3.1

Published

A type-safe React Query integration for Hono applications that provides seamless data fetching, mutations, and caching with full TypeScript support.

Readme

Type-safe React Query integration for Hono applications

A type-safe React Query integration for Hono applications that provides seamless data fetching, mutations, and caching with full TypeScript support.

Features

  • 🔒 Type-safe: Full TypeScript support with automatic type inference from your Hono routes
  • React Query integration: Built on top of TanStack Query for powerful caching and synchronization
  • 🎯 Hono-first: Designed specifically for Hono applications with automatic client generation
  • 🚀 Zero configuration: Works out of the box with your existing Hono setup
  • 🔄 Optimistic updates: Built-in support for optimistic UI updates
  • 📦 Lightweight: Minimal bundle size with no unnecessary dependencies

Installation

pnpm add @softnetics/hono-react-query
bun add @softnetics/hono-react-query
yarn add @softnetics/hono-react-query
npm install @softnetics/hono-react-query

Peer Dependencies

This package requires the following peer dependencies:

pnpm add @tanstack/react-query hono
bun add @tanstack/react-query hono
yarn add @tanstack/react-query hono
npm install @tanstack/react-query hono

Quick Start

1. Define your Hono app

// app.ts
import { Hono } from 'hono'
import { validator } from 'hono/validator'
import { z } from 'zod'

const app = new Hono()
  .get('/users', (c) => {
    return c.json({ users: [{ id: 'id', name: 'John Doe' }] }, 200)
  })
  .get('/users/:id', (c) => {
    if (!c.req.param('id')) {
      return c.json({ error: 'User ID is required' }, 400)
    }
    return c.json({ user: { id: 'id', name: 'John Doe' } }, 200)
  })
  .post('/users', validator('json', z.object({ name: z.string() })), async (c) => {
    const body = await c.req.valid('json')
    if (body.name === 'forbidden_word') {
      return c.json({ error: 'Name is required' }, 400)
    }
    return c.json({ user: { id: 'id', name: body.name } }, 201)
  })

export type AppType = typeof app

2. Create the React Query client

// client.ts
import { createReactQueryClient } from '@softnetics/hono-react-query'
import type { App } from './app'

export const reactQueryClient = createReactQueryClient<App>({
  baseUrl: 'http://localhost:3000',
})

3. Use in your React components

// components/UserList.tsx
import { reactQueryClient } from './client'
import { isHonoResponseError } from '@softnetics/hono-react-query'

export function UserList() {
  // Type-safe query with automatic inference
  const usersQuery = reactQueryClient.useQuery('/users', '$get', {})

  usersQuery.data // { data: { users: { id: string; name: string }[] }, status: 200, format: 'json' } | undefined
  usersQuery.error // Error | HonoResponseError<{ error: string }, 400, 'json'> | null

  // Type-safe mutation with automatic inference
  const createUserMutation = reactQueryClient.useMutation('/users', '$post', {
    onMutate: (data) => {
      return { toastId: toast.loading('Creating user...') }
    },
    onSuccess: (response, _, context) => {
      response.data // { user: { id: string; name: string } }
      response.status // 201
      response.format // 'json'
      toast.success('User created', { id: context.toastId })
    },
    onError: (error, _, context) => {
      error // Error | HonoResponseError<{ error: string }, 400, 'json'>
      if (isHonoResponseError(error)) {
        error.data // { error: string }
        error.status // 400
        error.format // 'json'
      }
      toast.error(error.data.error, { id: context?.toastId })
    },
  })

  createUserMutation.mutateAsync({ json: { name: 'John Doe' } })
}

API Reference

createReactQueryClient<T>(options)

Creates a type-safe React Query client for your Hono application. Under the hood, it uses the hc function to create a client for your Hono application.

Parameters:

  • options.baseUrl - The base URL of your Hono application
  • options - Additional Hono client options (headers, fetch options, etc.)

Returns: A client object with the following methods:

Query Methods

useQuery(path, method, payload, options?)

Executes a GET request with React Query caching. Under the hood, it uses the useQuery function to execute the query.

const { data, isLoading, error } = reactQueryClient.useQuery(
  '/users',
  '$get',
  { input: { query: { limit: 10 } } },
  { staleTime: 5 * 60 * 1000 } // 5 minutes
)

queryOptions(path, method, payload, options?)

Creates query options for use with useQuery or useSuspenseQuery. Under the hood, it uses the queryOptions function to create the query options.

import { reactQueryClient } from './client'
import { useQuery } from '@tanstack/react-query'

const queryOptions = reactQueryClient.queryOptions('/users', '$get', {})
const { data } = useQuery(queryOptions) // automatically inferred type based on your Hono app

Mutation Methods

useMutation(path, method, options?, mutationOptions?)

Mutates data on the server. Under the hood, it uses the useMutation function to execute the mutation.

const createUserMutation = reactQueryClient.useMutation('/users', '$post', {
  onMutate: (data) => {
    return { toastId: toast.loading('Creating user...') }
  },
  onSuccess: (response, _, context) => {
    response.data // { user: { id: string; name: string } }
    response.status // 201
    response.format // 'json'
    toast.success('User created', { id: context.toastId })
  },
  onError: (error, _, context) => {
    error // Error | HonoResponseError<{ error: string }, 400, 'json'>
    if (isHonoResponseError(error)) {
      error.data // { error: string }
      error.status // 400
      error.format // 'json'
    }
    toast.error(error?.data?.error, { id: context?.toastId })
  },
})

mutationOptions(path, method, options?, mutationOptions?)

Creates mutation options for use with useMutation. Under the hood, it uses the mutationOptions function to create the mutation options.

const mutationOptions = reactQueryClient.mutationOptions('/users', '$post')
const createUserMutation = useMutation(mutationOptions) // automatically inferred type based on your Hono app

Cache Management

useGetQueryData(path, method, payload)

Gets cached type-safe query data without triggering a fetch. Under the hood, it uses the getQueryData function to get the cached data.

const getQueryData = reactQueryClient.useGetQueryData('/users', '$get', {})
const cachedData = getQueryData()
// cachedData is typed as: { data: { users: { id: string; name: string }[] }, status: 200, format: 'json' } | undefined

useSetQueryData(path, method, payload)

Manually updates cached query data with a type-safe payload. Under the hood, it uses the setQueryData function to update the cached data.

const setQueryData = reactQueryClient.useSetQueryData('/users', '$get', {})

function onSubmit(newData: { users: { id: string; name: string }[] }) {
  setQueryData(newData)
}

useInvalidateQueries(path, method, payload?, options?)

Invalidates cached queries to trigger refetching with a type-safe payload. Under the hood, it uses the invalidateQueries function to invalidate the cached data.

// Exact key
const invalidateQueries = reactQueryClient.useInvalidateQueries('/users', '$get')
invalidateQueries() // Refetch all user queries

// Exact key
const invalidateQueries = reactQueryClient.useInvalidateQueries('/users/:id', '$get', {
  input: { param: { id: '1' } },
})
invalidateQueries() // Invalidate the user query with the id parameter

// Partial key
const invalidateQueries = reactQueryClient.useInvalidateQueries('/users/:id', '$get')
invalidateQueries() // Invalidate all queries starting with ["/users/:id", "$get"]

useOptimisticUpdateQuery(path, method, payload)

Performs optimistic updates with rollback capability. Under the hood, it uses the getQueryData and setQueryData functions to perform the optimistic update.

const optimisticUpdate = reactQueryClient.useOptimisticUpdateQuery('/users', '$get', {})

// combine with useMutation
const { mutate } = reactQueryClient.useMutation('/users', '$post', {
  onMutate: (data) => {
    const updater = optimisticUpdate((prev) => ({
      ...prev,
      users: [...prev.users, data.json],
    }))
    return { updater }
  },
  onSuccess: (response, _, context) => {
    // handle success
  },
  onError: (error, _, context) => {
    // revert the optimistic update if the mutation fails
    context?.updater?.revert()
  },
})

Type Safety

The library provides full type safety by inferring types from your Hono application:

// Your Hono app types are automatically inferred
const { data } = reactQueryClient.useQuery('/users', '$get', {})
// data is typed as: "{ data: { users: User[] }, status: number, format: 'json' } | undefined"

const mutation = reactQueryClient.useMutation('/users', '$post')
mutation.mutate({ json: { name: 'John Doe' } }) // Expect type "{ json: { name: 'John Doe' } } | undefined" for payload

Error Handling

The library includes a custom error class for handling Hono responses:

import { HonoResponseError, isHonoResponseError } from '@softnetics/hono-react-query'

const { data, error } = reactQueryClient.useQuery('/users', '$get', {})

// Use "isHonoResponseError" to check if the error is a Hono response error and get the status, data, and format
if (error && isHonoResponseError(error)) {
  console.log('Status:', error.status)
  console.log('Data:', error.data)
  console.log('Format:', error.format)
}

Key Management

The library automatically generates query keys based on the path, method, and payload. You can access the key generation function:

const queryOptions = reactQueryClient.queryOptions('/users', '$get', {
  input: { query: { limit: 10 } },
})
const queryKey = queryOptions.queryKey //  ["/users", "$get", { query: { limit: 10 } }]

Limitation

Users must always specify the return status from the Hono app. If not specified, the library will not be able to infer the correct type.

const app = new Hono().get('/users', (c) => {
  return c.json({ users: [{ id: 'id', name: 'John Doe' }] }, 200) // "200" is required
})

License

MIT

Contributors

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.