Description
The RPC client accepts a custom fetch implementation via options, but the ClientResponse types are tightly coupled to the native fetch API. This makes it impractical to use popular fetch wrappers like ofetch, ky, or axios without significant type workarounds.
Current Behavior
When using a custom fetch client, the ClientResponse types still expect native fetch methods like res.ok, res.json(), res.text(), etc. However, custom fetch clients have different APIs and behaviors:
- ofetch: Throws errors on non-2xx responses instead of returning a response object with
ok: false
- ky: Similar behavior with a different API surface
- axios: Uses
data property instead of methods like .json()
Example
Here's a real-world case using ofetch in a Nuxt application:
import { createClient } from '@sdk/v1'
import type { ClientResponse } from 'hono/client'
export default function useApi() {
const client = createClient('', {
fetch: useAuthFetch(), // ofetch-based custom fetcher
})
return client
}
// Workaround: Type utility to extract 200 response since ofetch throws on non-2xx
export function res200<T>(
res: T,
): Extract<T, { status: 200 }> extends ClientResponse<infer Body, 200, any>
? Body
: Extract<T, { status: 200 }> {
return res as any
}
// Usage - must use .then(res200) after every RPC call
const result = await client.v1.orders.$get({ query: q }).then(res200)
The custom fetch implementation:
export function useAuthFetch() {
const instance = $fetch.create({ // ofetch
baseURL: '/api',
onRequest({ options }) {
// Add auth headers
},
async onResponseError({ response }) {
// Handle 401, refresh tokens, etc.
// ofetch throws here for non-2xx responses
},
})
return instance
}
Problem
- The
ClientResponse type assumes native fetch behavior
- Custom fetch clients have different APIs and error handling strategies
- TypeScript doesn't know that non-2xx responses will never arrive as normal response objects when using
ofetch
- Users must create type workarounds like
res200() for every RPC call
- This defeats the purpose of having type-safe RPC calls
Impact
- Loss of type safety when using custom fetch clients
- Boilerplate workarounds required for every API call
- Custom fetch clients (which are essential for auth, retries, interceptors) become impractical
- The
fetch option in RPC client is advertised but not truly usable with real-world fetch implementations
Possible Solutions
-
Generic type parameter for fetch response type
type ClientResponse<TBody, TStatus, TFormat, TFetchClient = 'native'> = ...
-
Separate type for custom fetch clients
type CustomClientResponse<TBody, TStatus> = TBody
-
Configuration option to specify fetch behavior
createClient('/', {
fetch: customFetch,
fetchBehavior: 'throw-on-error' // or 'native'
})
-
Response transformer function
Allow users to provide a type-safe transformer that maps their fetch client's response to a known shape
Environment
- Hono version: Latest
- Custom fetch client: ofetch (part of Nuxt/Nitro ecosystem)
- Use case: Nuxt 4 application with authentication, token refresh, and error handling
Related
This affects anyone using:
- ofetch (Nuxt/Nitro)
- ky
- axios
- Any custom fetch wrapper with authentication/retry logic
The current workaround significantly reduces the developer experience and type safety that Hono's RPC client promises to deliver.
Description
The RPC client accepts a custom
fetchimplementation via options, but theClientResponsetypes are tightly coupled to the native fetch API. This makes it impractical to use popular fetch wrappers likeofetch,ky, oraxioswithout significant type workarounds.Current Behavior
When using a custom fetch client, the
ClientResponsetypes still expect native fetch methods likeres.ok,res.json(),res.text(), etc. However, custom fetch clients have different APIs and behaviors:ok: falsedataproperty instead of methods like.json()Example
Here's a real-world case using
ofetchin a Nuxt application:The custom fetch implementation:
Problem
ClientResponsetype assumes native fetch behaviorofetchres200()for every RPC callImpact
fetchoption in RPC client is advertised but not truly usable with real-world fetch implementationsPossible Solutions
Generic type parameter for fetch response type
Separate type for custom fetch clients
Configuration option to specify fetch behavior
Response transformer function
Allow users to provide a type-safe transformer that maps their fetch client's response to a known shape
Environment
Related
This affects anyone using:
The current workaround significantly reduces the developer experience and type safety that Hono's RPC client promises to deliver.