Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

fruchtzwerg
Copy link
Contributor

This PR brings Vue Query in line with React Query v4/v5 support.

v4 gets a small update to its types to address #703
v5 mostly copies react-query-v5 with the necessary adjustments for Vue

Notable changes:

  • Vue uses a plugin system instead of React's Context Providers (see vue-query-v5/src/v5/create-ts-rest-plugin.ts).
  • Some internals are re-exported for more flexibility in case users want to create their own plugin-factory or composables.
  • Guards are re-exported with support for Vue's MaybeRef<T> type.
  • SuspenseQueries are not needed in Vue.

I have not looked at Nuxt/SSR support, yet. This is about as fully featured as I can currently make it.

Tested in one of my own projects via local npm link.

Please let me know if something is amiss.

Copy link

changeset-bot bot commented Oct 28, 2024

🦋 Changeset detected

Latest commit: 8ba1a04

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

This PR includes changesets to release 14 packages
Name Type
@ts-rest/vue-query Minor
@ts-rest/vue-query-v5 Minor
@ts-rest/nest Minor
@ts-rest/example-contracts Minor
@ts-rest/non-strict-mode-test Minor
@ts-rest/core Minor
@ts-rest/express Minor
@ts-rest/fastify Minor
@ts-rest/next Minor
@ts-rest/open-api Minor
@ts-rest/react-query-v5 Minor
@ts-rest/react-query Minor
@ts-rest/serverless Minor
@ts-rest/solid-query 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

Copy link

vercel bot commented Oct 28, 2024

@fruchtzwerg is attempting to deploy a commit to the ts-rest Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

nx-cloud bot commented Oct 28, 2024

View your CI Pipeline Execution ↗ for commit 8ba1a04.

Command Status Duration Result
nx affected -t lint,test,build ✅ Succeeded 3m 22s View ↗
nx run-many --target=build --projects=ts-rest* ✅ Succeeded 1m View ↗

☁️ Nx Cloud last updated this comment at 2025-04-06 18:47:32 UTC

Copy link

@oliverbutler
Copy link
Collaborator

Hi @fruchtzwerg!

Thanks so much for the PR, I didn't have a huge role in the implementation of the v5 react query adapter from @Gabrola but I've put reviewing this on my shortlist.

I'll try get on it shortly.

Appreciate your patience!

@oliverbutler oliverbutler self-assigned this Feb 10, 2025
@AlexGilleranGP
Copy link

Great work @fruchtzwerg - I ended up pulling down your branch and testing it against my (proprietary, sorry) code - the V5 types work great but I'm finding with the V4 types I'm getting Type instantiation is excessively deep and possibly infinite.ts(2589) when using useMutation. Removing the <Context> type param from here got it back under control.

The way that I integrated it is very weird (pretty much copy-pasting the code into my own fork) so it might have been a problem of my own creation, but thought I'd mention it in case someone else runs into the same problem and ends up searching for what caused it the same way I did.

Copy link

@AlexGilleranGP AlexGilleranGP left a comment

Choose a reason for hiding this comment

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

I've added some comments based on my experience pulling this down and using it on my codebase, hope I'm not intruding :).

The other bit of feedback I have is that making it so that you have to get the query client out of a composable makes it quite difficult to use e.g. fetchQuery in routes. An escape hatch where you can statically get a client as long as your client args stay static would be great.

Comment on lines 51 to 60
fetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData> &
TsRestQueryOptions<TAppRoute, TClientArgs>,
): Promise<TData>;

prefetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData, QueryKey> &
TsRestQueryOptions<TAppRoute, TClientArgs>,
): Promise<void>;

Choose a reason for hiding this comment

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

Suggested change
fetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData> &
TsRestQueryOptions<TAppRoute, TClientArgs>,
): Promise<TData>;
prefetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData, QueryKey> &
TsRestQueryOptions<TAppRoute, TClientArgs>,
): Promise<void>;
fetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey> &
TsRestQueryOptions<TAppRoute, TClientArgs, TQueryKey>,
): Promise<TData>;
prefetchQuery<TData = TQueryFnData>(
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey> &
TsRestQueryOptions<TAppRoute, TClientArgs, TQueryKey>,
): Promise<void>;

Choose a reason for hiding this comment

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

if you have a non-unknown[] typed query key this won't accept it for fetchQuery currently, this change fixes it for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you @AlexGilleranGP for your feedback.

Apparently I had missed the contextual function types here as well.
Wouldn't have caught this otherwise. 👍

> {
mutate: AppRouteFunction<TAppRoute, TClientArgs>;

useMutation<TContext = unknown>(
Copy link

@AlexGilleranGP AlexGilleranGP Apr 3, 2025

Choose a reason for hiding this comment

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

I'm finding that even with the v5 types, on a route that's sufficiently nested (e.g. users.current.totp.setEnabled), without removing this generic I fall victim to TS2589: Type instantiation is excessively deep and possibly infinite when calling useMutation.

I assume this is because routes on the contract end up being a deep recursive type (RecursivelyApplyOptions<RecursivelyApplyOptions<RecursivelyApplyOptions<) for every level of nesting, then TsRestVueQueryHooksContainer adds another n layers of recursive type to it (no idea why removing <Context> helps however, I assume that's just the straw that breaks the camel's back).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. I'd rather not fix this in the initial release of v5, beacause these types are copied straight from the react-query implementation. Therefore these issues will probably exist in react as well.
    To allow the maintainers to copy future updates straight into vue-query, I tried to maintain as much consistency as possible. This will increase the chances that the vue-query implementation will not end up as abandoneware.

  2. Removing the generic for the context here would also remove typesafety for the onMutate() result:

client.createPost.useMutation({
    onMutate: async (variables) => ({ title: variables.body.title }), // <-- define context
    onSuccess: (_data, _variables, context) => {
      console.log(context.title); // <-- context is of type { title: string }
    },
  });

If this is an issue for you and you cannot flatten your contract definition, I'd suggest opening a new issue after the release vue-query v5, so it can be addressed for both react and vue.

Copy link

sonarqubecloud bot commented Apr 6, 2025

@fruchtzwerg
Copy link
Contributor Author

fruchtzwerg commented Apr 6, 2025

The other bit of feedback I have is that making it so that you have to get the query client out of a composable makes it quite difficult to use e.g. fetchQuery in routes. An escape hatch where you can statically get a client as long as your client args stay static would be great.

Could you provide an example of what you are trying to achieve?

The TsRestPlugin only relies on provide()/inject() not on any templates. As long as you register TsRestPlugin (and therefore VueQueryPlugin) before the router, you should be able to use the useClient() composable in any guard or anywhere else in your routes.

Here is an example.

The vanilla Tanstack VueQueryPlugin also relies on inject() and must be present for ts-rest to work at all. Unless I'm missing something, to get a client statically, you would have to build your own version of useBaseQuery.ts that does not rely on vue dependency injection.

@AlexGilleranGP
Copy link

Thanks for taking a look @fruchtzwerg :)

Right now I get a QueryClient with new QueryClient like this, then I can export that around to where I need it. Similar with the ts rest client - previously I was getting an instance from initQueryClient and exporting/importing that too.

It is possible that I should be able to use the composable in my route handling and I just haven't got it configured correctly, but trying to invoke useClient in a router.beforeEach handler definitely fails.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.