Skip to content

Migrating to v3

v3 is a full rewrite of the code generator on top of a ts-morph pipeline, but the generated output is intentionally kept compatible with v2. For most projects, migrating means updating the package and regenerating — no code changes.

  1. Update the package and regenerate your client:

    Terminal window
    npm install -D @7nohe/openapi-react-query-codegen@^3
    npx openapi-rq -i ./petstore.yaml
  2. Be aware of the one change the compiler cannot catch: error responses now reject instead of silently resolving undefined data. If your UI already handles TanStack Query error states, nothing to do.

  3. If you use infinite queries (useXxxInfinite), fix the type errors the compiler reports — see below. They are mechanical.

  4. Everything else is compatible — you are done.

  • All generated hooks (useQuery, useSuspenseQuery, useMutation), prefetch and ensureQueryData functions, key constants, and key functions keep their v2 names and signatures.
  • JSDoc comments (including @deprecated) are still emitted on every hook.
  • Environment requirements are mostly unchanged from v2.2.0: Node.js 22.18+, typescript 5.x or 6.x, ts-morph 28.x. commander 12.x through 15.x are now all accepted.
  • @tanstack/react-query 5.x is now declared as a peer dependency (#134). It was always required at runtime by the generated code; the declaration just makes your package manager enforce it.

Every GET operation now also gets a queryOptions factory in queryOptions.ts, and paginatable operations get an infiniteQueryOptions factory. These are additive — existing code is unaffected.

import { useQuery } from "@tanstack/react-query";
import { findPetsOptions } from "../openapi/queries";
const { data } = useQuery(findPetsOptions({ query: { limit: 10 } }));

Infinite query hooks also accept initialPageParam and getNextPageParam overrides now (#156, #146), so custom pagination schemes no longer require editing the generated code:

const { data } = useFindPaginatedPetsInfinite({ query: { limit: 10 } }, undefined, {
initialPageParam: "",
getNextPageParam: (lastPage) => lastPage.meta?.cursor,
});

The infinite query family is now complete (#155):

  • prefetchUseXxxInfinite(queryClient, clientOptions, options?) — prefetch the first page (or more, via options.pages) on the server for SSR/Next.js hydration
  • useXxxSuspenseInfinite(clientOptions, queryKey?, options?) — Suspense variant sharing the same cache key as useXxxInfinite

Prefetch and ensure functions also take TanStack Query options now (#157):

await prefetchUseFindPets(queryClient, {}, { staleTime: 5_000 });
const pets = await ensureUseFindPetsData(queryClient, {}, { revalidateIfStale: true });

Behavior change: error responses now reject

Section titled “Behavior change: error responses now reject”

Generated query/mutation functions now call the SDK with throwOnError: true (#172). In v2, an error response silently resolved with undefined data (the hey-api runtime default is throwOnError: false), which broke ensureQueryData at runtime and never surfaced errors to TanStack Query. In v3, error responses reject, so:

  • isError / error on hooks now actually fire on HTTP error responses
  • ensureUseXxxData rejects instead of caching undefined
  • mutation onError callbacks fire as the types always claimed

If you relied on errors being swallowed, handle them via TanStack Query’s error state or try/catch around ensure* calls.

In v2, an infinite query shared its cache key with the plain query for the same operation, which corrupted the cache when both were used (#140). v3 gives infinite queries their own keys and types.

The cache key changed from ["FindPaginatedPets"] (shared with the plain query) to ["FindPaginatedPets", "infinite"]. Because the plain key stays the first segment, prefix matching now gives you granular invalidation (#174):

// Invalidate both the plain AND the infinite cache entries of the operation
queryClient.invalidateQueries({ queryKey: [useFindPaginatedPetsKey] });
// Invalidate only the infinite entries
// (useFindPaginatedPetsInfiniteKey is already an array — don't wrap it)
queryClient.invalidateQueries({ queryKey: useFindPaginatedPetsInfiniteKey });
// Target one exact query, params included
queryClient.setQueryData(UseFindPaginatedPetsInfiniteKeyFn(options), updater);

Note that useFindPaginatedPetsInfiniteKey is now a readonly ["FindPaginatedPets", "infinite"] tuple instead of a string. If you built keys manually from the string constant, use the exported key functions instead.

If you persist the query cache (e.g. persistQueryClient), previously cached infinite data will not match the new key and will be refetched once after the upgrade.

2. The page parameter is no longer accepted in clientOptions

Section titled “2. The page parameter is no longer accepted in clientOptions”

Infinite hooks now take a dedicated options type that excludes the page parameter — TanStack Query supplies it through the pageParam mechanism. In v2 a page value passed here was silently overwritten, so removing it does not change behavior:

const { data, fetchNextPage } = useFindPaginatedPetsInfinite({
query: { page: 1, tags: [], limit: 10 },
query: { tags: [], limit: 10 },
});

This also fixes compilation for OpenAPI specs where the page parameter is required.

3. Specs with a required page parameter now compile

Section titled “3. Specs with a required page parameter now compile”

No action needed — this was previously a compile error in the generated code. The generated XxxInfiniteClientOptions type makes the page parameter unnecessary while keeping all other parameters typed as before.