From f8dad6b2e11d3e9b5b71be0fd62d507e7c73fad0 Mon Sep 17 00:00:00 2001 From: Anon-136 Date: Thu, 15 Jan 2026 16:35:47 +0700 Subject: [PATCH 1/3] feat: return client and originalClient from createRQClient --- src/index.spec.tsx | 38 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 19 +++++++++++++++++++ src/types.ts | 18 +++++++++++++++++- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/index.spec.tsx b/src/index.spec.tsx index 58b5452..3772c55 100644 --- a/src/index.spec.tsx +++ b/src/index.spec.tsx @@ -38,6 +38,8 @@ describe('createReactQueryClient', () => { baseUrl: 'http://localhost:3000', }) + expect(client.honoClient).toBeDefined() + expect(client.client).toBeDefined() expect(client.useQuery).toBeDefined() expect(client.useMutation).toBeDefined() expect(client.queryOptions).toBeDefined() @@ -48,6 +50,42 @@ describe('createReactQueryClient', () => { expect(client.useOptimisticUpdateQuery).toBeDefined() }) + describe('client', () => { + it('should have typed arguments ', () => { + const client = createReactQueryClient({ + baseUrl: 'http://localhost:3000', + }) + const queryFn = () => + client.client('/users/:id', '$get', { + param: { id: 'none' }, + }) + + expectTypeOf>().toEqualTypeOf< + Promise< + | { + data: { + user: { + id: string + name: string + } + } + status: 200 + format: 'json' + headers: Record + } + | { + data: { + error: string + } + status: 400 + format: 'json' + headers: Record + } + > + >() + }) + }) + describe('useQuery', () => { it('should contain Error in Data when throwOnError is false', () => { const client = createReactQueryClient({ diff --git a/src/index.ts b/src/index.ts index a712f0d..8ab3000 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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, @@ -40,8 +42,11 @@ function createReactQueryClient( : 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), @@ -94,6 +99,18 @@ async function responseParser(response: Response, throwOnError?: boolean): Promi throw new HonoResponseError(res) } +function flatClientFactory>( + honoClient: Record> +): FlatClientFn { + 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 isThrowOnError = options?.throwOnError ?? true + const response = await handler(payload, options) + return responseParser(response, isThrowOnError) + }) as FlatClientFn +} + function useQueryFactory>( client: Record> ): UseHonoQuery { @@ -259,6 +276,8 @@ function useOptimisticUpdateQueryFactory>() { export { createQueryKey, createReactQueryClient, + type Client as FlatClient, + type FlatClientFn, HonoResponseError, type InferUseHonoQuery, isHonoResponseError, diff --git a/src/types.ts b/src/types.ts index 2b91596..86de0ac 100644 --- a/src/types.ts +++ b/src/types.ts @@ -56,7 +56,7 @@ type ErrorResponse = > : never -type HonoPayloadOptions = ClientRequestOptions & { throwOnError?: boolean } +export type HonoPayloadOptions = ClientRequestOptions & { throwOnError?: boolean } export type HonoPayload< TInput, TOptions extends HonoPayloadOptions | undefined = HonoPayloadOptions | undefined, @@ -305,6 +305,8 @@ export type UseHonoOptimisticUpdateQuery> = < | undefined export type ReactQueryClient> = { + honoClient: Record> + client: FlatClientFn useQuery: UseHonoQuery useSuspenseQuery: UseHonoSuspenseQuery useMutation: UseHonoMutation @@ -319,3 +321,17 @@ export type ReactQueryClient> = { export type InferUseHonoQuery> = TQuery extends UseQueryResult ? TData : never + +export type FlatClientFn> = < + TPath extends keyof TApp = keyof TApp, + TMethod extends keyof TApp[TPath] = keyof TApp[TPath], + TPayload extends InferFunctionInput = InferFunctionInput< + TApp[TPath][TMethod] + >, + TOptions extends HonoPayloadOptions | undefined = HonoPayloadOptions | undefined, +>( + path: TPath, + method: TMethod, + payload: TPayload, + options?: TOptions +) => Promise>> From 06e87ce7ebcba6856dad6fadc3a3043b46237df5 Mon Sep 17 00:00:00 2001 From: Anon-136 Date: Thu, 15 Jan 2026 16:49:48 +0700 Subject: [PATCH 2/3] fix: throwOnError should be false by default --- src/index.spec.tsx | 31 ++++++++++++++++++++++--------- src/index.ts | 4 ++-- src/types.ts | 6 +++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/index.spec.tsx b/src/index.spec.tsx index 3772c55..21f6694 100644 --- a/src/index.spec.tsx +++ b/src/index.spec.tsx @@ -51,15 +51,11 @@ describe('createReactQueryClient', () => { }) describe('client', () => { - it('should have typed arguments ', () => { - const client = createReactQueryClient({ - baseUrl: 'http://localhost:3000', - }) - const queryFn = () => - client.client('/users/:id', '$get', { - param: { id: 'none' }, - }) - + const client = createReactQueryClient({ + baseUrl: 'http://localhost:3000', + }) + it('should have typed arguments with return type', () => { + const queryFn = () => client.client('/users/:id', '$get', { param: { id: 'none' } }) expectTypeOf>().toEqualTypeOf< Promise< | { @@ -84,6 +80,23 @@ describe('createReactQueryClient', () => { > >() }) + it('should not contain Error when throwOnError is true', () => { + const queryFn = () => + client.client('/users/:id', '$get', { param: { id: 'none' } }, { throwOnError: true }) + expectTypeOf>().toEqualTypeOf< + Promise<{ + data: { + user: { + id: string + name: string + } + } + status: 200 + format: 'json' + headers: Record + }> + >() + }) }) describe('useQuery', () => { diff --git a/src/index.ts b/src/index.ts index 8ab3000..c2da827 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,9 +105,9 @@ function flatClientFactory>( 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 isThrowOnError = options?.throwOnError ?? true + const throwOnError = options?.throwOnError ?? false // Note: Default to false for flat client const response = await handler(payload, options) - return responseParser(response, isThrowOnError) + return responseParser(response, throwOnError) }) as FlatClientFn } diff --git a/src/types.ts b/src/types.ts index 86de0ac..dec7413 100644 --- a/src/types.ts +++ b/src/types.ts @@ -334,4 +334,8 @@ export type FlatClientFn> = < method: TMethod, payload: TPayload, options?: TOptions -) => Promise>> +) => Promise< + TOptions extends { throwOnError: true } + ? SuccessResponse> + : ClientResponseParser> +> From 305f2d451483c15f5b350b3c7572a863f21b74cc Mon Sep 17 00:00:00 2001 From: Anon Jindawong <51981658+Anon-136@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:59:43 +0700 Subject: [PATCH 3/3] Create clean-phones-agree.md --- .changeset/clean-phones-agree.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/clean-phones-agree.md diff --git a/.changeset/clean-phones-agree.md b/.changeset/clean-phones-agree.md new file mode 100644 index 0000000..40edbf7 --- /dev/null +++ b/.changeset/clean-phones-agree.md @@ -0,0 +1,5 @@ +--- +"@softnetics/hono-react-query": minor +--- + +return client and originalClient from createRQClient