From 78aca80b21dfa32c956869cc99612684c3441c2d Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:23:09 +0700 Subject: [PATCH 1/7] feat(openapi-react-query): improve useInfiniteQuery's typesafety --- packages/openapi-react-query/src/index.ts | 11 ++- .../openapi-react-query/test/index.test.tsx | 88 +------------------ 2 files changed, 9 insertions(+), 90 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 855b249c9..5ce93a027 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -22,8 +22,9 @@ import type { MaybeOptionalInit, Client as FetchClient, DefaultParamsOption, + ParamsOption, } from "openapi-fetch"; -import type { HttpMethod, MediaType, PathsWithMethod, RequiredKeysOf } from "openapi-typescript-helpers"; +import type { FilterKeys, HttpMethod, MediaType, PathsWithMethod, RequiredKeysOf } from "openapi-typescript-helpers"; // Helper type to dynamically infer the type from the `select` property type InferSelectReturnType = TSelect extends (data: TData) => infer R ? R : TData; @@ -117,7 +118,9 @@ export type UseInfiniteQueryMethod, "queryKey" | "queryFn" > & { - pageParamName?: string; + pageParamName: ParamsOption>["params"] extends { query: infer Query } + ? keyof Query + : never; }, >( method: Method, @@ -216,12 +219,12 @@ export default function createClient useSuspenseQuery(queryOptions(method, path, init as InitWithUnknowns, options), queryClient), useInfiniteQuery: (method, path, init, options, queryClient) => { - const { pageParamName = "cursor", ...restOptions } = options; + const { pageParamName, ...restOptions } = options; const { queryKey } = queryOptions(method, path, init); return useInfiniteQuery( { queryKey, - queryFn: async ({ queryKey: [method, path, init], pageParam = 0, signal }) => { + queryFn: async ({ queryKey: [method, path, init], pageParam, signal }) => { const mth = method.toUpperCase() as Uppercase; const fn = client[mth] as ClientMethod; const mergedInit = { diff --git a/packages/openapi-react-query/test/index.test.tsx b/packages/openapi-react-query/test/index.test.tsx index 65420164a..171b904c0 100644 --- a/packages/openapi-react-query/test/index.test.tsx +++ b/packages/openapi-react-query/test/index.test.tsx @@ -862,6 +862,7 @@ describe("client", () => { { getNextPageParam: (lastPage) => lastPage.nextPage, initialPageParam: 0, + pageParamName: "cursor", }, ), { wrapper }, @@ -947,6 +948,7 @@ describe("client", () => { { getNextPageParam: (lastPage) => lastPage.nextPage, initialPageParam: 0, + pageParamName: "cursor", select: (data) => ({ pages: [...data.pages].reverse(), pageParams: [...data.pageParams].reverse(), @@ -1001,91 +1003,5 @@ describe("client", () => { const allItems = result.current.data?.pages.flatMap((page) => page.items); expect(allItems).toEqual([4, 5, 6, 1, 2, 3]); }); - it("should use custom cursor params", async () => { - const fetchClient = createFetchClient({ baseUrl }); - const client = createClient(fetchClient); - - // First page request handler - const firstRequestHandler = useMockRequestHandler({ - baseUrl, - method: "get", - path: "/paginated-data", - status: 200, - body: { items: [1, 2, 3], nextPage: 1 }, - }); - - const { result, rerender } = renderHook( - () => - client.useInfiniteQuery( - "get", - "/paginated-data", - { - params: { - query: { - limit: 3, - }, - }, - }, - { - getNextPageParam: (lastPage) => lastPage.nextPage, - initialPageParam: 0, - pageParamName: "follow_cursor", - }, - ), - { wrapper }, - ); - - // Wait for initial query to complete - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - - // Verify first request - const firstRequestUrl = firstRequestHandler.getRequestUrl(); - expect(firstRequestUrl?.searchParams.get("limit")).toBe("3"); - expect(firstRequestUrl?.searchParams.get("follow_cursor")).toBe("0"); - - // Set up mock for second page before triggering next page fetch - const secondRequestHandler = useMockRequestHandler({ - baseUrl, - method: "get", - path: "/paginated-data", - status: 200, - body: { items: [4, 5, 6], nextPage: 2 }, - }); - - // Fetch next page - await act(async () => { - await result.current.fetchNextPage(); - // Force a rerender to ensure state is updated - rerender(); - }); - - // Wait for second page to be fetched and verify loading states - await waitFor(() => { - expect(result.current.isFetching).toBe(false); - expect(result.current.hasNextPage).toBe(true); - expect(result.current.data?.pages).toHaveLength(2); - }); - - // Verify second request - const secondRequestUrl = secondRequestHandler.getRequestUrl(); - expect(secondRequestUrl?.searchParams.get("limit")).toBe("3"); - expect(secondRequestUrl?.searchParams.get("follow_cursor")).toBe("1"); - - expect(result.current.data).toBeDefined(); - expect(result.current.data?.pages[0].nextPage).toBe(1); - - expect(result.current.data).toBeDefined(); - expect(result.current.data?.pages[1].nextPage).toBe(2); - - // Verify the complete data structure - expect(result.current.data?.pages).toEqual([ - { items: [1, 2, 3], nextPage: 1 }, - { items: [4, 5, 6], nextPage: 2 }, - ]); - - // Verify we can access all items through pages - const allItems = result.current.data?.pages.flatMap((page) => page.items); - expect(allItems).toEqual([1, 2, 3, 4, 5, 6]); - }); }); }); From 3e1bc36c00550ec057c1067a4e1dfcbe1810862f Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:38:22 +0700 Subject: [PATCH 2/7] docs: update useInfiniteQuery example --- docs/openapi-react-query/use-infinite-query.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/openapi-react-query/use-infinite-query.md b/docs/openapi-react-query/use-infinite-query.md index 86bcbad1e..1b19dac67 100644 --- a/docs/openapi-react-query/use-infinite-query.md +++ b/docs/openapi-react-query/use-infinite-query.md @@ -35,6 +35,7 @@ const PostList = () => { }, { getNextPageParam: (lastPage) => lastPage.nextPage, + pageParamName: "cursor", initialPageParam: 0, } ); From 3aeefab456f8042bd4e3464e114d04cc49b3865e Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:05:50 +0700 Subject: [PATCH 3/7] feat(openapi-react-query): make initialPageParam typesafe --- packages/openapi-react-query/src/index.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 5ce93a027..d0e503528 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -107,6 +107,8 @@ export type UseInfiniteQueryMethod, Init extends MaybeOptionalInit, Response extends Required>, + Query extends ParamsOption>["params"] extends { query: infer Query } ? Query : never, + PageParamName extends keyof Query, Options extends Omit< UseInfiniteQueryOptions< Response["data"], @@ -114,19 +116,18 @@ export type UseInfiniteQueryMethod, Response["data"], QueryKey, - unknown + Query[PageParamName] >, - "queryKey" | "queryFn" - > & { - pageParamName: ParamsOption>["params"] extends { query: infer Query } - ? keyof Query - : never; - }, + "queryKey" | "queryFn" | "initialPageParam" + >, >( method: Method, url: Path, init: InitWithUnknowns, - options: Options, + options: Options & { + pageParamName: PageParamName; + initialPageParam: Query[PageParamName]; + }, queryClient?: QueryClient, ) => UseInfiniteQueryResult, Response["error"]>; From bd874bd756e32678e761d49b7ea7836a8dbb3c64 Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:15:16 +0700 Subject: [PATCH 4/7] refactor(openapi-react-query): simplify types --- packages/openapi-react-query/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index d0e503528..6df62fa42 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -118,7 +118,7 @@ export type UseInfiniteQueryMethod, Query[PageParamName] >, - "queryKey" | "queryFn" | "initialPageParam" + "queryKey" | "queryFn" >, >( method: Method, @@ -126,7 +126,6 @@ export type UseInfiniteQueryMethod, options: Options & { pageParamName: PageParamName; - initialPageParam: Query[PageParamName]; }, queryClient?: QueryClient, ) => UseInfiniteQueryResult, Response["error"]>; From 3c26c952d6041cbef9a19b07acc9dbd178157c9f Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:21:07 +0700 Subject: [PATCH 5/7] Revert "refactor(openapi-react-query): simplify types" This reverts commit bd874bd756e32678e761d49b7ea7836a8dbb3c64. --- packages/openapi-react-query/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 6df62fa42..d0e503528 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -118,7 +118,7 @@ export type UseInfiniteQueryMethod, Query[PageParamName] >, - "queryKey" | "queryFn" + "queryKey" | "queryFn" | "initialPageParam" >, >( method: Method, @@ -126,6 +126,7 @@ export type UseInfiniteQueryMethod, options: Options & { pageParamName: PageParamName; + initialPageParam: Query[PageParamName]; }, queryClient?: QueryClient, ) => UseInfiniteQueryResult, Response["error"]>; From 21a300cc7a2f3806c1e17cca54696f2cb18110c8 Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:13:45 +0700 Subject: [PATCH 6/7] fix(openapi-react-query): make initialPageParam non-nullable --- packages/openapi-react-query/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index d0e503528..57a841450 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -126,7 +126,7 @@ export type UseInfiniteQueryMethod, options: Options & { pageParamName: PageParamName; - initialPageParam: Query[PageParamName]; + initialPageParam: NonNullable; }, queryClient?: QueryClient, ) => UseInfiniteQueryResult, Response["error"]>; From 565f502ad67b87424057ab2d7b3e9fd519e8bfdc Mon Sep 17 00:00:00 2001 From: patrickariel <161032380+patrickariel@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:07:51 +0700 Subject: [PATCH 7/7] refactor(openapi-react-query): streamline UseInfiniteQueryMethod --- packages/openapi-react-query/src/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 57a841450..1f58497c2 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -116,9 +116,9 @@ export type UseInfiniteQueryMethod, Response["data"], QueryKey, - Query[PageParamName] + NonNullable >, - "queryKey" | "queryFn" | "initialPageParam" + "queryKey" | "queryFn" >, >( method: Method, @@ -126,7 +126,6 @@ export type UseInfiniteQueryMethod, options: Options & { pageParamName: PageParamName; - initialPageParam: NonNullable; }, queryClient?: QueryClient, ) => UseInfiniteQueryResult, Response["error"]>;