Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize default query keys #243

Open
mpetito-envative opened this issue Nov 1, 2021 · 33 comments
Open

Customize default query keys #243

mpetito-envative opened this issue Nov 1, 2021 · 33 comments
Assignees
Labels
tanstack-query TanStack Query related issue workaround A workaround has been provided

Comments

@mpetito-envative
Copy link

mpetito-envative commented Nov 1, 2021

Is it possible to configure the convention used for query key generation?

I see that you can override the query key when using the generated hook, but this requires remembering to do so each time. Instead, I'd like to configure how the query key is constructed project-wide because our APIs already have consistent hierarchical routes.

For ex., instead of this generated query key:

export const getGetResourceByIdQueryKey = (resourceId: number,) => [`/resource/${resourceId}`];

It would make a lot more sense in my project for the query key to be split by path segments, e.g.:

['resource', resourceId]

I don't see any obvious way to configure or override the convention used for query keys.

My motivation is to simplify query invalidation by having predictable query keys and support prefix invalidation.

@anymaniax
Copy link
Collaborator

Hello @mpetito-envative, not really the only thing that you can do at the moment is to override the options in the orval config. Could be a new option but how would you do it?

@anymaniax anymaniax added the enhancement New feature or request label Nov 1, 2021
@mpetito-envative
Copy link
Author

It seems like this would be a runtime behavior because the queryKey depends on properties passed into the query hook.

It is already possible to do something similar with the mutator override for the purpose of replacing the http request. Perhaps a queryKey override would allow for replacement of the queryKeyFn at runtime?

Alternatively (or in addition), it might make sense to change the default convention used for queryKey generation. I know this would be a breaking change, but I can't think of any disadvantage to splitting the route on / and it would offer an easy path for prefix invalidation if routes already represent a hierarchy.

@anymaniax
Copy link
Collaborator

It's also what I thought but if we do a sort of mutator I will need to pass the route and all params to the function. For the other one, could make sense but since it's a breaking change I will add it for the next major release

@vezaynk
Copy link

vezaynk commented Apr 5, 2022

@anymaniax Any updates on this? This issue just bit me and I'm not sure how to move forward without hardcoding the path in some form and matching with a predicate.

@missing1984
Copy link

missing1984 commented Jul 10, 2022

+1. This is an awesome tool but the queryKey is blocking me from using it. Is there a workaround before it get addressed?

@anymaniax
Copy link
Collaborator

@missing1984 it's on my todo list for the next version. Sorry I have a lot of stuff to do lately

@OliverDudgeon
Copy link
Contributor

OliverDudgeon commented Aug 24, 2022

Another use case for query-key customisation is when there are more than one orval-generated api clients. If both have a /version endpoint for example, then the queries keys clash.

@la55u
Copy link

la55u commented Oct 25, 2022

Did you guys manage to find a workaround?

@missing1984
Copy link

I wrote a custom script base on "ts-morph" to process the generated file. It will be great to have native support though

@vezaynk
Copy link

vezaynk commented Nov 16, 2022

@missing1984 Can you share it?

@missing1984
Copy link

@knyzorg sure, https://gist.github.com/missing1984/907c2b6aef0206e8944ccbaea7c4d0a9 the script basically break key into arrays

@missing1984
Copy link

I found a changelog item in 6.11. Is this possible now, I can't find an example in the doc. @anymaniax

@anymaniax
Copy link
Collaborator

anymaniax commented Feb 10, 2023

Hello @missing1984, you can do it with this and you can do the same with the property queryKey only. It’s completely new and the doc is not complete about it but it’s like a mutator.

@OliverDudgeon
Copy link
Contributor

Has anyone got this solution working? My queryOptions function isn't run by orval.

@anymaniax
Copy link
Collaborator

What is your config @OliverDudgeon?

@OliverDudgeon
Copy link
Contributor

OliverDudgeon commented Feb 28, 2023

The queryOptions mutator function is used in the generated file but not imported.

I have this block in my config

{
	query: {
		useQuery: true,
		queryOptions: './src/queryMutator.ts',
	},
}

mutator is the default export of that file:

// src/queryMutator.ts
export const mutator = (args: any) => {
  console.log('Mutator:');
  console.log(args);
};

export default mutator;

In generated hooks,

const customOptions = getAssetQueryOptionsMutator({
  ...queryOptions,
  queryKey,
  queryFn,
});

is generated but ts errors on getAssetQueryOptionsMutator as it isn't imported.

If I use

{
	query: {
		useQuery: true,
		queryOptions: {
			path: './src/queryMutator.ts',
			name: 'mutator',
		},
	},
}
then this is generated in hooks

```ts
const customOptions = mutator({ ...queryOptions, queryKey, queryFn });

but mutator isn't imported.

There isn't anything in the orval logs.

@anymaniax
Copy link
Collaborator

@OliverDudgeon you have the last version of orval?

@OliverDudgeon
Copy link
Contributor

Using 6.12.0

@anymaniax
Copy link
Collaborator

I am currently trying on the react basic samples with this config and it seems to work properly.

import { defineConfig } from 'orval';

export default defineConfig({
  petstore: {
    output: {
      target: 'src/api/endpoints/petstoreFromFileSpecWithTransformer.ts',
      schemas: 'src/api/model',
      client: 'react-query',
      override: {
        query: {
          useQuery: true,
          queryOptions: {
            path: './src/api/mutator/queryMutator.ts',
            name: 'mutator',
          },
        },
      },
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

@anymaniax
Copy link
Collaborator

@OliverDudgeon Do You have extra information that can help me reproduce this?

@OliverDudgeon
Copy link
Contributor

Don't have time tonight to figure out a minimal example but here is what I have.

This is my project, https://github.com/InformaticsMatters/squonk2-openapi-js-client-generator.

  1. Update orval to @latest
  2. Place this OpenApi, https://pastebin.com/EsN5cVUW, in the root as openapi.yaml
  3. Add the mutator file below this
  4. Add a path to it in queryOptions
  5. Run npm i && npm run orval
// // src/queryMutator.ts
export const mutator = (args: any) => {
  console.log('Mutator:');
  console.log(args);

  return args;
};

export default mutator;

@anymaniax
Copy link
Collaborator

Hello @OliverDudgeon, I can reproduce a problem only when I use the path directly like the following. Otherwise it seems to work properly

{
 query: {
  queryOptions: './src/queryMutator'
}
}

@anymaniax
Copy link
Collaborator

@OliverDudgeon I did a fix will be in the next release

@OliverDudgeon
Copy link
Contributor

OliverDudgeon commented Mar 2, 2023

Ok this is even more strange. On 6.12.0, the error only happens on some of the generated files. I'm using mode: 'tags-split'. Two of my tags have the import statement missing when the others are fine.

I will try get your recent commit working to test it out.

@OliverDudgeon
Copy link
Contributor

Great, the update on master seems to fix the issue. Cheers!

@missing1984
Copy link

missing1984 commented Aug 11, 2023

hmm, i did try out the mutator but i found the getXXXQueryKey function remains unchanged. We're rely on this function to do cache invalidation..

Any idea how can i override the key getter function? @anymaniax

@soartec-lab
Copy link
Member

Hi, @missing1984

In the version 6.24.0 it is possible to override keys by specifying options in the custom hook that is generated. Could you please check this?

@AndrejNemec
Copy link

AndrejNemec commented Jul 15, 2024

+1

hmm, i did try out the mutator but i found the getXXXQueryKey function remains unchanged. We're rely on this function to do cache invalidation..

Any idea how can i override the key getter function? @anymaniax

I want to change the individual keys so that when I call getXXXQueryKey, it directly returns the key according to how I modify it. The idea is that I want to use getXXXQueryKey with a new key.

For example:

From:

export const getApiQueryKey = (params?: GetApiKeyParams,) => {
    return [`/api/apikey/`, ...(params ? [params]: [])] as const;
    }

To:

export const getApiQueryKey = (params?: GetApiKeyParams,) => {
    return [`MARKETS_SERVICE`, `/api/apikey/`, ...(params ? [params]: [])] as const;
    }

and use in:

await queryClient.prefetchQuery(getApiQueryKey(...))

The reason is that I have 2 generated SDKs and some keys are in conflict. To ensure their uniqueness, I need to add a prefix to the keys array, for example, the name of the service for which we are generating the API. This way, I can use both generated APIs in the app without the keys conflicting with each other.

@g-otn
Copy link

g-otn commented Aug 2, 2024

@missing1984 @AndrejNemec

For now I've created a script which searches for the getXXXQueryKey function return array and naively replaces that part of the code with a hardcoded string prefix:

https://gist.github.com/g-otn/76ed6c228b042597f54b20731d888857

You can use this script with the afterAllFilesWrite hook option, which sends the generated files (and folders) as args.

It seems to work well for now (Orval v6.31.0), but it'll probably break in the future.

Make sure to run it before prettier/etc.

@melloware melloware added the workaround A workaround has been provided label Aug 2, 2024
@EgorPopovPP
Copy link

EgorPopovPP commented Aug 18, 2024

@melloware
Hi!
I think that best and fast solution - create option that can split route in array

Now:

const routeString = isVue(outputClient)
      ? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this
      : `\`${route}\``;

Suggest:

const routeString = isVue(outputClient) || options.splitQueryKeyRoute
      ? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this
      : `\`${route}\``;

https://github.com/orval-labs/orval/blob/852dce7746b33a246e408b4284a1f5f71caf4390/packages/query/src/index.ts#L1185C4-L1187C24

@melloware
Copy link
Collaborator

PR is welcome.

@EgorPopovPP
Copy link

EgorPopovPP commented Aug 19, 2024

PR is welcome.

I need your help)). I don't understand how to build project to see my generated code with my fix.
Read more in PR

@tortilaman
Copy link

tortilaman commented Aug 27, 2024

For anyone stuck on this, I got the custom default queryKey generation working using the queryOptions field as stated above. This approach does not change the get[operationName]QueryKey function itself, but it does does update the use[operationName]QueryOptions hook, which is what is actually used by React Query when it's determining the query keys.

An example:

// orval.config.ts
import { defineConfig } from 'orval';

export default defineConfig({
  name: {
    output: {
      [...]
      override: {
        query: {
          queryOptions: {
            path: './src/api/mutator/queryMutator.ts',
            name: 'mutator',
          },
        },
      },
    },
    [...]
  },
});
// src/api/mutator/queryOptions.ts
export const mutator = (args: any) => {
  const params = new URLSearchParams(window.location.search)
  const scope = params.get('scope')

  return {...args, queryKey: [`${args.queryKey[0]}?scope=${scope}`, args.queryKey[1]}
}

export default mutator

The mutator is evaluated at runtime, so you can use the window object, etc. If you put a breakpoint in the mutator function and run your site, you'll see what you're getting for args.

If you were using get[OperationName]QueryKey to invalidate / cancel / set things in the cache, this won't work, as it is using Orval's "default", and not the "default" that you set in queryOptions. You'll need to use use[operationName]QueryOptions.queryKey instead. i.e.

// From this
queryClient.invalidateQueries({ queryKey: getSomeOperaitonQueryKey() })
// To this
const { queryKey } = useSomeOperationQueryOptions()
queryClient.invalidateQueries({ queryKey })

That being said, If you're doing this you're likely doing optimistic updates using the cache, and the arguments for those change, so this likely won't work for you. So assuming you're not doing optimistic updates directly on the cache, this should be a viable workaround for you.

@melloware melloware added tanstack-query TanStack Query related issue and removed enhancement New feature or request labels Aug 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tanstack-query TanStack Query related issue workaround A workaround has been provided
Projects
None yet
Development

No branches or pull requests