Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clean-phones-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@softnetics/hono-react-query": minor
---

return client and originalClient from createRQClient
51 changes: 51 additions & 0 deletions src/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import { createReactQueryClient, HonoResponseError } from '.'

const basicHonoApp = new Hono()

Check warning on line 14 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'basicHonoApp' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
.get('/users', (c) => {
return c.json({ users: [{ id: 'id', name: 'John Doe' }] }, 200)
})
Expand All @@ -38,6 +38,8 @@
baseUrl: 'http://localhost:3000',
})

expect(client.honoClient).toBeDefined()
expect(client.client).toBeDefined()
expect(client.useQuery).toBeDefined()
expect(client.useMutation).toBeDefined()
expect(client.queryOptions).toBeDefined()
Expand All @@ -48,13 +50,62 @@
expect(client.useOptimisticUpdateQuery).toBeDefined()
})

describe('client', () => {
const client = createReactQueryClient<BasicHonoApp>({
baseUrl: 'http://localhost:3000',
})
it('should have typed arguments with return type', () => {
const queryFn = () => client.client('/users/:id', '$get', { param: { id: 'none' } })

Check warning on line 58 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryFn' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
expectTypeOf<ReturnType<typeof queryFn>>().toEqualTypeOf<
Promise<
| {
data: {
user: {
id: string
name: string
}
}
status: 200
format: 'json'
headers: Record<string, string>
}
| {
data: {
error: string
}
status: 400
format: 'json'
headers: Record<string, string>
}
>
>()
})
it('should not contain Error when throwOnError is true', () => {
const queryFn = () =>

Check warning on line 84 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryFn' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
client.client('/users/:id', '$get', { param: { id: 'none' } }, { throwOnError: true })
expectTypeOf<ReturnType<typeof queryFn>>().toEqualTypeOf<
Promise<{
data: {
user: {
id: string
name: string
}
}
status: 200
format: 'json'
headers: Record<string, string>
}>
>()
})
})

describe('useQuery', () => {
it('should contain Error in Data when throwOnError is false', () => {
const client = createReactQueryClient<BasicHonoApp>({
baseUrl: 'http://localhost:3000',
})

const queryOptions = client.queryOptions('/users/:id', '$get', {

Check warning on line 108 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryOptions' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
input: {
param: { id: 'none' },
},
Expand Down Expand Up @@ -88,7 +139,7 @@
>
>()

const queryFn = () =>

Check warning on line 142 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryFn' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
client.useQuery('/users/:id', '$get', {
input: { param: { id: 'none' } },
options: { throwOnError: false },
Expand Down Expand Up @@ -125,7 +176,7 @@
baseUrl: 'http://localhost:3000',
})

const queryOptions = client.queryOptions('/users/:id', '$get', {

Check warning on line 179 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryOptions' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
input: {
param: { id: 'none' },
},
Expand All @@ -148,7 +199,7 @@
>
>()

const queryFn = () =>

Check warning on line 202 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryFn' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
client.useQuery('/users/:id', '$get', {
input: { param: { id: 'none' } },
options: { throwOnError: true },
Expand Down Expand Up @@ -186,7 +237,7 @@
baseUrl: 'http://localhost:3000',
})

const queryOptions = client.suspenseQueryOptions('/users/:id', '$get', {

Check warning on line 240 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryOptions' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
input: {
param: { id: 'none' },
},
Expand Down Expand Up @@ -220,7 +271,7 @@
>
>()

const queryFn = () =>

Check warning on line 274 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryFn' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
client.useSuspenseQuery('/users/:id', '$get', {
input: { param: { id: 'none' } },
options: { throwOnError: false },
Expand Down Expand Up @@ -257,7 +308,7 @@
baseUrl: 'http://localhost:3000',
})

const queryOptions = client.suspenseQueryOptions('/users/:id', '$get', {

Check warning on line 311 in src/index.spec.tsx

View workflow job for this annotation

GitHub Actions / ci

'queryOptions' is assigned a value but only used as a type. Allowed unused vars must match /^_/u
input: {
param: { id: 'none' },
},
Expand Down
19 changes: 19 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { HonoResponseError, isHonoResponseError } from './error'
import { createQueryKey } from './query-key'
import {
type Client,
type FlatClientFn,
type HonoMutationOptions,
type HonoPayloadOptions,
type HonoQueryOptions,
type HonoSuspenseQueryOptions,
type InferUseHonoQuery,
Expand All @@ -40,8 +42,11 @@ function createReactQueryClient<T extends Hono>(
: never
> {
const client = hc(options.baseUrl, options)
const flatClient = flatClientFactory(client)

return {
client: flatClient,
honoClient: client,
useQuery: useQueryFactory(client),
useSuspenseQuery: useSuspenseQueryFactory(client),
useMutation: useMutationFactory(client),
Expand Down Expand Up @@ -94,6 +99,18 @@ async function responseParser(response: Response, throwOnError?: boolean): Promi
throw new HonoResponseError(res)
}

function flatClientFactory<THono extends Record<string, any>>(
honoClient: Record<string, ClientRequest<any>>
): FlatClientFn<THono> {
return (async (path: string, method: string, payload?: any, options?: HonoPayloadOptions) => {
const paths = [...path.toString().split('/').filter(Boolean), method.toString()]
const handler = getter(honoClient, paths)
const throwOnError = options?.throwOnError ?? false // Note: Default to false for flat client
const response = await handler(payload, options)
return responseParser(response, throwOnError)
}) as FlatClientFn<THono>
}

function useQueryFactory<T extends Record<string, any>>(
client: Record<string, ClientRequest<any>>
): UseHonoQuery<T> {
Expand Down Expand Up @@ -259,6 +276,8 @@ function useOptimisticUpdateQueryFactory<T extends Record<string, any>>() {
export {
createQueryKey,
createReactQueryClient,
type Client as FlatClient,
type FlatClientFn,
HonoResponseError,
type InferUseHonoQuery,
isHonoResponseError,
Expand Down
22 changes: 21 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type ErrorResponse<T> =
>
: never

type HonoPayloadOptions = ClientRequestOptions & { throwOnError?: boolean }
export type HonoPayloadOptions = ClientRequestOptions & { throwOnError?: boolean }
export type HonoPayload<
TInput,
TOptions extends HonoPayloadOptions | undefined = HonoPayloadOptions | undefined,
Expand Down Expand Up @@ -305,6 +305,8 @@ export type UseHonoOptimisticUpdateQuery<TApp extends Record<string, any>> = <
| undefined

export type ReactQueryClient<TApp extends Record<string, any>> = {
honoClient: Record<string, ClientRequest<any>>
client: FlatClientFn<TApp>
useQuery: UseHonoQuery<TApp>
useSuspenseQuery: UseHonoSuspenseQuery<TApp>
useMutation: UseHonoMutation<TApp>
Expand All @@ -319,3 +321,21 @@ export type ReactQueryClient<TApp extends Record<string, any>> = {

export type InferUseHonoQuery<TQuery extends UseQueryResult<any, any>> =
TQuery extends UseQueryResult<infer TData, any> ? TData : never

export type FlatClientFn<TApp extends Record<string, any>> = <
TPath extends keyof TApp = keyof TApp,
TMethod extends keyof TApp[TPath] = keyof TApp[TPath],
TPayload extends InferFunctionInput<TApp[TPath][TMethod]> = InferFunctionInput<
TApp[TPath][TMethod]
>,
TOptions extends HonoPayloadOptions | undefined = HonoPayloadOptions | undefined,
>(
path: TPath,
method: TMethod,
payload: TPayload,
options?: TOptions
) => Promise<
TOptions extends { throwOnError: true }
? SuccessResponse<InferFunctionReturn<TApp[TPath][TMethod]>>
: ClientResponseParser<InferFunctionReturn<TApp[TPath][TMethod]>>
>