Skip to content

feat(@tanstack/query): add queryOptions override parameter to generated useQuery hooks#3528

Open
nmokkenstorm wants to merge 1 commit into
hey-api:mainfrom
nmokkenstorm:feat/use-query-options
Open

feat(@tanstack/query): add queryOptions override parameter to generated useQuery hooks#3528
nmokkenstorm wants to merge 1 commit into
hey-api:mainfrom
nmokkenstorm:feat/use-query-options

Conversation

@nmokkenstorm

@nmokkenstorm nmokkenstorm commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Summary

When useQuery hook generation is enabled, generated hooks now accept an optional queryOptions property that is spread over the computed query options, which is the same as the existing mutationOptions override on useMutation hooks. This lets consumers override TanStack Query options without losing type safety.

Independent of #3926 (skipToken support). Whichever PR merges first, the other will need a small useQuery.ts rebase to combine both features.

@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel

vercel Bot commented Mar 9, 2026

Copy link
Copy Markdown

@nmokkenstorm is attempting to deploy a commit to the Hey API Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Mar 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9e85de8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hey-api/openapi-ts Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 6d07fd5 to cf8e859 Compare March 9, 2026 15:55
@codecov

codecov Bot commented Mar 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 39.00%. Comparing base (795a33d) to head (9e85de8).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3528      +/-   ##
==========================================
+ Coverage   38.68%   39.00%   +0.32%     
==========================================
  Files         626      627       +1     
  Lines       21864    21879      +15     
  Branches     6406     6408       +2     
==========================================
+ Hits         8458     8534      +76     
+ Misses      10922    10871      -51     
+ Partials     2484     2474      -10     
Flag Coverage Δ
unittests 39.00% <100.00%> (+0.32%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new

pkg-pr-new Bot commented Mar 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/@hey-api/codegen-core@3528

@hey-api/json-schema-ref-parser

npm i https://pkg.pr.new/@hey-api/json-schema-ref-parser@3528

@hey-api/nuxt

npm i https://pkg.pr.new/@hey-api/nuxt@3528

@hey-api/openapi-ts

npm i https://pkg.pr.new/@hey-api/openapi-ts@3528

@hey-api/shared

npm i https://pkg.pr.new/@hey-api/shared@3528

@hey-api/spec-types

npm i https://pkg.pr.new/@hey-api/spec-types@3528

@hey-api/types

npm i https://pkg.pr.new/@hey-api/types@3528

@hey-api/vite-plugin

npm i https://pkg.pr.new/@hey-api/vite-plugin@3528

commit: 9e85de8

@mrlubos

mrlubos commented Mar 9, 2026

Copy link
Copy Markdown
Member

@nmokkenstorm Why is it an optional configuration flag? Why not add it for everyone?

@mrlubos

mrlubos commented Mar 9, 2026

Copy link
Copy Markdown
Member

@nandorojo will like this

@mrlubos

mrlubos commented Mar 9, 2026

Copy link
Copy Markdown
Member

@nmokkenstorm I updated your description with linked issue

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@nmokkenstorm Why is it an optional configuration flag? Why not add it for everyone?

habit to make it opt-in tbh. I think the skipToken is a breaking change too on type level and it also slightly increases the generated bundle size but I will check.

@mrlubos

mrlubos commented Mar 9, 2026

Copy link
Copy Markdown
Member

@nmokkenstorm I didn't look at the code yet, stopped after seeing the configuration haha. If it's going to be breaking either way, I'd much rather deprecate the old approach because we wouldn't want new users to start with the flag disabled, realize one day they need to enable it, and face a lot of breaking changes. We can talk through it when you feel the pull request is more ready. I'm pretty sure there was another thread where we talked about skip tokens but can't find it, I'd like to ideally avoid another breaking change when that thread gets addressed

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@nmokkenstorm I didn't look at the code yet, stopped after seeing the configuration haha. If it's going to be breaking either way, I'd much rather deprecate the old approach because we wouldn't want new users to start with the flag disabled, realize one day they need to enable it, and face a lot of breaking changes. We can talk through it when you feel the pull request is more ready. I'm pretty sure there was another thread where we talked about skip tokens but can't find it, I'd like to ideally avoid another breaking change when that thread gets addressed

I'll double check, I think it's definately doable to have it be backwards compatible without a breaking change. The useQuery/Mutation stuff was opt in so I stuck to the pattern but I think this can be done without losing ergonomics.

I'll update the pr description and ping you if it's reviewable, I needed a remote public ref to do some internal testing with first anyways.

Thanks for the feedback!

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 7df42fb to 3007b3c Compare March 10, 2026 08:27
@mrlubos

mrlubos commented Mar 10, 2026

Copy link
Copy Markdown
Member

@nmokkenstorm Can you clarify what do you mean by "add useQuery hooks" in the title? React/Preact Query already support generating useQuery hooks so I'm confused what it's referencing

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@nmokkenstorm Can you clarify what do you mean by "add useQuery hooks" in the title? React/Preact Query already support generating useQuery hooks so I'm confused what it's referencing

its about having the generated useQuery hooks exist per entity, with queryoptions and skiptoken enabled. the reason it's named like that is because the original code/branch orginates from a fork that pre-dates the support we had in our project.

still tinkering on this, nothing to see/review here yet!

@nmokkenstorm nmokkenstorm changed the title [draft] add useQuery hooks and skipToken support [draft] expose query options and skiptoken on generated useQuery hooks Mar 10, 2026
@mrlubos

mrlubos commented Mar 10, 2026

Copy link
Copy Markdown
Member

Just making sure we're aligned on the scope! I shall retreat once more

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch 3 times, most recently from d6ebc7c to b1fc4b9 Compare March 20, 2026 08:55
@nmokkenstorm nmokkenstorm changed the title [draft] expose query options and skiptoken on generated useQuery hooks feat: add skipToken support and queryOptions override to TanStack Query useQuery hooks Mar 20, 2026
@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from b1fc4b9 to 2ffe100 Compare March 24, 2026 15:48
@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch 5 times, most recently from d8bbba0 to 5eb6a92 Compare April 3, 2026 11:04
@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@mrlubos I think it does what it needs to do now, below a redacted diff of how we're currently using it in the fork.

I ran into an issue where the auto paginated version of useQuery has ts-ignore inserted and I needed it somewhere else to stay compliant, so I added support for a new case to the DSL for that. Probably worthwhile to check a) if that's correct and b) if maybe we can improve the related code to no longer need the ts-ignore, but that seemed a bit out of scope for this PR.

lmk what you think? I'll see if I can get the merge checks to behave a bit more but I'm not sure what I actually can and can not influence there, you mentioned they are a bit flaky before.


Before (query)

import { getSomeResourceOptions } from '@org/api';
import { useQuery } from '@tanstack/react-query';

const { data, isLoading } = useQuery({
  ...getSomeResourceOptions({ path: { id: id ?? '' } }),
  enabled: !!id,
});

After (query)

import { useGetSomeResource } from '@org/api';
import { skipToken } from '@tanstack/react-query';

const { data, isLoading } = useGetSomeResource(
  id ? { path: { id } } : skipToken
);

Before (query with extra options)

import { getStatusOptions } from '@org/api';
import { useQuery } from '@tanstack/react-query';

const { data, isLoading } = useQuery({
  ...getStatusOptions(),
  enabled: shouldPoll,
  refetchInterval: shouldPoll ? 5000 : false,
});

After (query with extra options)

import { useGetStatus } from '@org/api';

const { data, isLoading } = useGetStatus({
  queryOptions: {
    enabled: shouldPoll,
    refetchInterval: shouldPoll ? 5000 : false,
  },
});

Before (mutation)

import { updateResourceMutation, getResourceQueryKey } from '@org/api';
import { useMutation, useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();
const { mutate, isPending } = useMutation({
  ...updateResourceMutation(),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: getResourceQueryKey() });
  },
});

After (mutation)

import { useUpdateResource, getResourceQueryKey } from '@org/api';
import { useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();
const { mutate, isPending } = useUpdateResource({
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: getResourceQueryKey() });
  },
});

@nmokkenstorm nmokkenstorm marked this pull request as ready for review April 7, 2026 09:17
@pullfrog

pullfrog Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Reviewed PR #3528. The skipToken support and queryOptions override implementation is well-structured and type-safe. Flagged the useQuery default change from false to true as a breaking change that needs explicit acknowledgment, and noted a dead symbol registration (UseQueryOptions/CreateQueryOptions) in plugin.ts.

Task list (5/5 completed)
  • Read diff to understand the scope of changes
  • Review core implementation files (plugin, useQuery, queryOptions, config, types)
  • Review test changes
  • Check snapshot changes for correctness
  • Self-critique and submit review

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no issues found.

Task list (5/5 completed)
  • Read diff - source code changes (plugin logic, config, types)
  • Read diff - test changes
  • Read diff - snapshot changes (sampling)
  • Trace data flow and verify correctness
  • Self-critique and submit review

Pullfrog  | View workflow run𝕏

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@mrlubos I see some conflicts have crept in, happy to resolve them; do you need anything else to get this reviewed / merged while I'm at it?

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch 2 times, most recently from 51a80e4 to a791fdd Compare May 5, 2026 16:08
@DogPawHat

DogPawHat commented May 22, 2026

Copy link
Copy Markdown

All in favor of the skip token work thanks.

One question: this auto-generates query hooks based on the query options and make that behavior the default. I'd actually prefer that the hooks NOT be generated by default. The query options do mostly everything we need and I think auto generated custom hooks are now abstraction dead weight in my opnion. To quote from https://tkdodo.eu/blog/the-query-options-api#query-factories

I would even take it to a point where custom hooks won’t be my first choice for abstractions. They seem a bit pointless if all they do is: const useTodos = () => useQuery(todosQuery)

Perfectly fine having the option to generate hooks but it's extra code that I think people should ask for directy.

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

All in favor of the skip token work thanks.

No problem!

One question: this auto-generates query hooks based on the query options and make that behavior the default.

This was the original behaviour, there was some discussion about this upthread.

I sort of I agree with what you're saying for different reasons, I think in an ideal scenario I either import the hook straight up from the generated file because I'm not doing something complicated. If I'm doing something complicated with the custom options (and probably/possibly reusing that throughout my app) I specific it separately by introducing a new file.

I agree with you that creating a file just for re-exporting the generated hook doesn't make for the best ergonomics.

@mrlubos and I had some talk here and elsewhere about BC and why the current approach makes sense, but I must say it has been a while and I reworked this code a bit over time so maybe if we talk again we might get to a different conclusion

Either way thanks for your insight! Happy to work a bit more on this if needed to get it merged, once in a while I rebase with main and fix any conflicts/issues that surface, I'd be more than happy to receive some instruction from the maintainers to see what we can do to get this in. Fwiw the project I'm working on that's appreciating the missing/added functionality by this PR got a lot leaner for it.

@mrlubos

mrlubos commented May 24, 2026

Copy link
Copy Markdown
Member

@nmokkenstorm is there a world where we could get the skipToken support without the hooks? Second everything else that was said above, my question is purely from the design perspective.

@DogPawHat

Copy link
Copy Markdown

@nmokkenstorm is there a world where we could get the skipToken support without the hooks? Second everything else that was said above, my question is purely from the design perspective.

I was a bit confused why the two where coupled together as well

@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@nmokkenstorm is there a world where we could get the skipToken support without the hooks? Second everything else that was said above, my question is purely from the design perspective.

Hmmm they're not necessarily intrinsically linked, although it does make sense to implement them at least in a way that behaves well together.

The way the skiptoken works is sort of as an override/overload to the query options, so any implementation for either should at least be aware of both. I can split it up in two separate (stacked?) PRs and explain the design decisions behind them if that makes reviewing easier.

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 291119b to 6ce8096 Compare May 25, 2026 12:14
@nmokkenstorm nmokkenstorm changed the title feat: add skipToken support and queryOptions override to TanStack Query useQuery hooks feat(@tanstack/query)!: enable useQuery hooks by default with queryOptions override May 25, 2026
@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 6ce8096 to 2410551 Compare May 25, 2026 13:19
@nmokkenstorm nmokkenstorm marked this pull request as draft May 25, 2026 13:20
@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch 4 times, most recently from e57f9a2 to c2b4e46 Compare May 25, 2026 16:51
@nmokkenstorm nmokkenstorm changed the title feat(@tanstack/query)!: enable useQuery hooks by default with queryOptions override feat(@tanstack/query): add queryOptions override parameter to generated useQuery hooks May 25, 2026
@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@nmokkenstorm is there a world where we could get the skipToken support without the hooks? Second everything else that was said above, my question is purely from the design perspective.

@mrlubos @DogPawHat it's split now, slightly unsure on the branch hygiene, would love you input. this branch is now query options only, the linked branch is skiptoken, I have half a thought to retarget this PR to the other branch, but I don't know how well that works with your review flow. Alternative is to just merge the other branch first, keep this in draft and I do the integration of both features.

Either way both need some additional testing on my end because the code diverges slightly from the original approach, but I think especially the skiptoken stuff should be fairly straightforward to test/review, lmk if you run into any issues!

@nmokkenstorm nmokkenstorm marked this pull request as ready for review May 29, 2026 14:02

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed changes — adds a queryOptions override parameter to generated useQuery hooks, mirroring the existing mutationOptions pattern on useMutation hooks. The change lets consumers pass TanStack Query options like staleTime or enabled per call site without losing type safety.

  • Introduce UseQueryParams<TFactory> type alias in packages/openapi-ts/src/plugins/@tanstack/query-core/shared/useQueryParams.ts — a shared utility type that intersects the SDK options with an optional queryOptions override typed as Partial<Omit<ReturnType<TFactory>, 'queryKey' | 'queryFn'>>.
  • Update createUseQuery emitter in packages/openapi-ts/src/plugins/@tanstack/query-core/v5/useQuery.ts — replaces the direct Options<TData> parameter with UseQueryParams<typeof optionsFn>, destructures queryOptions from the parameter, and spreads it after the computed options so overrides take precedence.
  • Add unit test in packages/openapi-ts/src/plugins/@tanstack/query-core/v5/__tests__/useQuery.test.ts — verifies the emitted AST contains the type alias and the correct hook body.
  • Add snapshot test scenario useQuery-enabled across OpenAPI 2.0.x, 3.0.x, and 3.1.x — covers generated output when useQuery: true is set in plugin config.
  • Add changeset for a minor version bump of @hey-api/openapi-ts.

Pullfrog  | View workflow run | Using Kimi K2𝕏

@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 7c1ecdb to 5517e49 Compare May 30, 2026 10:06
devin-ai-integration Bot added a commit to vellum-ai/vellum-assistant that referenced this pull request Jun 12, 2026
…an't accept TQ options

Generated useXxxQuery() hooks only accept SDK params (path, query, body)
and do not support TanStack Query options (enabled, select, staleTime).
Removing them eliminates a misleading API surface — queries should use
the factory pattern: useQuery({ ...xxxOptions(), enabled }).

Generated mutation hooks (useXxxMutation) remain enabled — they correctly
accept TQ callbacks (onSuccess, onError, onMutate, onSettled).

References:
- TanStack Query queryOptions pattern: https://tanstack.com/query/latest/docs/framework/react/guides/query-options
- Upstream fix tracking: hey-api/hey-api#3528

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ashleeradka added a commit to vellum-ai/vellum-assistant that referenced this pull request Jun 12, 2026
…yAPI config (#34724)

* docs(web): fix misleading query hook guidance — recommend factory pattern for queries

The docs incorrectly recommended useXxxQuery() hooks as the default for
queries. On HeyAPI v0.97+, generated query hooks only accept SDK params
(path, query, body) — they do not accept TanStack Query options like
enabled, select, or staleTime.

Updated CONVENTIONS.md, STATE_MANAGEMENT.md, and AGENTS.md to:
- Recommend useQuery({ ...xxxOptions(), enabled }) for queries
- Keep useXxxMutation() for mutations (they accept TQ callbacks)
- Document the limitation with link to upstream PR hey-api/hey-api#3528
- Fix all example code that showed enabled on generated query hooks

This aligns with TanStack Query's own recommended approach:
https://tanstack.com/query/latest/docs/framework/react/guides/query-options

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(web): align DaemonConfigPatch with generated ConfigPatchData body type

DaemonConfigPatch (hand-rolled) is structurally incompatible with
ConfigPatchData['body'] (generated index signature). This is a
pre-existing type error exposed when CI regenerates types from the
latest daemon spec.

Temporary fix — PR #34583 deletes this entire file by replacing
hand-rolled types with generated SDK types.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(web): disable useQuery in HeyAPI config — generated query hooks can't accept TQ options

Generated useXxxQuery() hooks only accept SDK params (path, query, body)
and do not support TanStack Query options (enabled, select, staleTime).
Removing them eliminates a misleading API surface — queries should use
the factory pattern: useQuery({ ...xxxOptions(), enabled }).

Generated mutation hooks (useXxxMutation) remain enabled — they correctly
accept TQ callbacks (onSuccess, onError, onMutate, onSettled).

References:
- TanStack Query queryOptions pattern: https://tanstack.com/query/latest/docs/framework/react/guides/query-options
- Upstream fix tracking: hey-api/hey-api#3528

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(web): set useQuery: false explicitly for clarity

The plugin skips generation when useQuery is omitted entirely, but
an explicit false makes the intent clearer and guards against any
future default changes.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@nmokkenstorm nmokkenstorm force-pushed the feat/use-query-options branch from 5517e49 to 9e85de8 Compare June 15, 2026 13:30
@nmokkenstorm

Copy link
Copy Markdown
Contributor Author

@ashleeradka I see you referenced this PR, seeing as its unmerged, do you have any additions we should keep in mind for usecase? I'd also be interested in knowing if the approach here works for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 🚀 Feature request. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants