A library for creating typesafe standardized query keys, useful for cache management in @tanstack/query

Overview

tRPC

Query Key Factory

Latest build Latest published version Bundlephobia Tree shaking available Types included License Number of downloads GitHub Stars

Typesafe query key management for @tanstack/query with auto-completion features.

Focus on writing and invalidating queries without the hassle of remembering
how you've set up a key for a specific query! This lib will take care of the rest.

๐Ÿ“ฆ Install

Query Key Factory is available as a package on NPM, install with your favorite package manager:

npm install @lukemorales/query-key-factory

โšก Quick start

Start by defining the query keys for the features of your app:

Declare your store in a single file

import { createQueryKeyStore } from "@lukemorales/query-key-factory";

// if your prefer to declare everything in one file
export const queryKeys = createQueryKeyStore({
  users: null,
  todos: {
    list: (filters: TodoFilters) => ({ filters }),
    search: (query: string, limit = 15) => [query, limit],
    todo: (todoId: string) => todoId,
  },
});

Fine-grained declaration colocated by features

import { createQueryKeys, mergeQueryKeys } from "@lukemorales/query-key-factory";

// queries/users.ts
export const usersKeys = createQueryKeys('users');

// queries/todos.ts
export const todosKeys = createQueryKeys('todos', {
  list: (filters: TodoFilters) => ({ filters }),
  search: (query: string, limit = 15) => [query, limit],
  todo: (todoId: string) => todoId,
});

// queries/index.ts
export const queryKeys = mergeQueryKeys(usersKeys, todosKeys);

Use throughout your codebase as the single source for writing the query keys for your cache management:

import { queryKeys } from '../queries';

export function useUsers() {
  return useQuery(queryKeys.users._def, fetchUsers);
};
import { queryKeys } from '../queries';

export function useTodos(filters: TodoFilters) {
  return useQuery(queryKeys.todos.list(filters), fetchTodos);
};

export function useSearchTodos(query: string, limit = 15) {
  return useQuery(queryKeys.todos.search(query, limit), fetchSearchTodos, {
    enabled: Boolean(query),
  });
};

export function useUpdateTodo() {
  const queryClient = useQueryClient();

  return useMutation(updateTodo, {
    onSuccess(newTodo) {
      queryClient.setQueryData(queryKeys.todos.todo(newTodo.id), newTodo);

      // invalidate all the list queries
      queryClient.invalidateQueries({
        queryKey: queryKeys.todos.list._def,
        refetchActive: false,
      });
    },
  });
};

๐Ÿ“ Features

Standardized keys

All keys generated follow the @tanstack/query standard of being an array at top level, including keys with serializable objects:

export const todosKeys = createQueryKeys('todos', {
  list: (filters: TodoFilters) => ({ filters }),
  search: (query: string, limit = 15) => [query, limit],
  todo: (todoId: string) => todoId,
});

// => createQueryKeys output:
// {
//   _def: ['todos'],
//   list: (filters: TodoFilters) => ['todos', 'list', { filters }],
//   search: (query: string, limit = 15) => ['todos', 'search', query, limit],
//   todo: (todoId: string) => ['todos', 'todo', todoId],
// }

Access to serializable keys scope definition

Easy way to access the serializable key scope and invalidade all cache for that context:

todosKeys.list({ status: 'completed' }) // => ['todos', 'list', { status: 'completed' }]
todosKeys.list._def; // => ['todos', 'list']

todosKeys.search('tanstack query', 15); // => ['todos', 'search', 'tanstack query', 15]
todosKeys.search._def; // => ['todos', 'search']

todosKeys.todo('todo-id'); // => ['todos', 'todo', 'todo-id']
todosKeys.todo._def; // => ['todos', 'todo']

Create a single point of access for all your query keys

Declare your query keys store in a single file

Just one place to edit and maintain your store:

export const queryKeys = createQueryKeyStore({
  users: null,
  todos: {
    list: (filters: TodoFilters) => ({ filters }),
    search: (query: string, limit = 15) => [query, limit],
    todo: (todoId: string) => todoId,
  },
});

// => createQueryKeyStore output:
// {
//   users: {
//     _def: ['users'],
//   },
//   todos: {
//     _def: ['todos'],
//     list: (filters: TodoFilters) => ['todos', 'list', { filters }],
//     search: (query: string, limit = 15) => ['todos', 'search', query, limit],
//     todo: (todoId: string) => ['todos', 'todo', todoId],
//   },
// };

Declare your query keys by feature

Have fine-grained control over your features' keys and merge them into a single object to have access to all your query keys in your codebase:

// queries/users.ts
const usersKeys = createQueryKeys('users');

// queries/todos.ts
const todosKeys = createQueryKeys('todos', {
  list: (filters: TodoFilters) => ({ filters }),
  search: (query: string, limit = 15) => [query, limit],
  todo: (todoId: string) => todoId,
});

// queries/index.ts
export const queryKeys = mergeQueryKeys(usersKeys, todosKeys);

// => mergeQueryKeys output:
// {
//   users: {
//     _def: ['users'],
//   },
//   todos: {
//     _def: ['todos'],
//     list: (filters: TodoFilters) => ['todos', 'list', { filters }],
//     search: (query: string, limit = 15) => ['todos', 'search', query, limit],
//     todo: (todoId: string) => ['todos', 'todo', todoId],
//   },
// };

Type safety and smart autocomplete

Typescript is a first class citizen of the Query Key Factory lib, providing easy of use and autocomplete for all query keys available and their outputs. Don't remember if a key is serializable or the shape of a key? Just mouseover and your IDE will show you all information you need to know.

Infer the type of the store's query keys

import { createQueryKeyStore, inferQueryKeyStore } from "@lukemorales/query-key-factory";

export const queryKeys = createQueryKeyStore({
  users: null,
  todos: {
    list: (filters: TodoFilters) => ({ filters }),
    search: (query: string, limit = 15) => [query, limit],
    todo: (todoId: string) => todoId,
  },
});

export type QueryKeys = inferQueryKeyStore<typeof queryKeys>;

// => QueryKeys type:
// {
//   users: {
//     _def: readonly ['users'];
//   };
//   todos: {
//     _def: readonly ['todos'];
//     list: readonly ['todos', 'list', { filters: TodoFilters }];
//     search: readonly ['todos', 'search', string, number];
//     todo: readonly ['todos', 'todo', string];
//   };
// }
import { createQueryKeys, inferQueryKeyStore } from "@lukemorales/query-key-factory";

// queries/users.ts
const usersKeys = createQueryKeys('users');

// queries/todos.ts
const todosKeys = createQueryKeys('todos', {
  list: (filters: TodoFilters) => ({ filters }),
  search: (query: string, limit = 15) => [query, limit],
  todo: (todoId: string) => todoId,
});

// queries/index.ts
export const queryKeys = mergeQueryKeys(usersKeys, todosKeys);

export type QueryKeys = inferQueryKeyStore<typeof queryKeys>;

Infer the type of a feature's query keys

import { createQueryKeys, inferQueryKeys } from "@lukemorales/query-key-factory";

export const todosKeys = createQueryKeys('todos', {
  list: (filters: TodoFilters) => ({ filters }),
  search: (query: string, limit = 15) => [query, limit],
  todo: (todoId: string) => todoId,
});

export type TodosKeys = inferQueryKeys<typeof todosKeys>;

Type your QueryFunctionContext with ease

Get accurate types of your query keys passed to the queryFn context:

import type { QueryKeys } from "../queries";
import type { TodosKeys } from "../queries/todos";

type TodosListQueryKey = QueryKeys['todos']['list'] | TodosKeys['list'];

const fetchTodos = async (ctx: QueryFunctionContext<TodosListQueryKey>) => {
  const [, , { filters }] = ctx.queryKey; // readonly ['todos', 'list', { filters }]

  const search = new URLSearchParams(filters);

  return fetch(`${BASE_URL}/todos?${search.toString()}`).then(data => data.json());
}

export function useTodos(filters: TodoFilters) {
  return useQuery(queryKeys.todos.list(filters), fetchTodos);
};
Comments
  • Nested keys required?

    Nested keys required?

    Hello, with React query i naturally have this scenario:

    -list of customers by account filtered by order by

    how i map this to cache keys is:

    `function customersByAccountId(accountId: number) { return ["customerListByAccountId", accountId]; }

    function customersByAccountIdFiltered(accountId: number, queryFilter: QueryFilter) { return [...customersByAccountId(accountId), "list", { queryFilter }]; }`

    This way, when i edit an attribute for a customer, i can: update the in memory query for customersByAccountIdFiltered (which will change the attribute on the current screen, without reload)

    and invalidate customersByAccountId (with refetchActive: false,) which will invalidate all other cached filtered entries.

    How do i map this with query-key-factory?

    enhancement 
    opened by kimbuba 11
  • I can't export my query keys. (I'm following the official docs).

    I can't export my query keys. (I'm following the official docs).

    H! I'm trying to use the library (v1.0.0 and v1.0.1) but I got this error with a simple implementation: Exported variable 'userQueries' has or is using name 'QueryFunctionContext' from external module ".../node_modules/@lukemorales/query-key-factory/dist/index" but cannot be named. ts(4023)

    Code:

    import { createQueryKeyStore } from "@lukemorales/query-key-factory";
    export const userQueries = createQueryKeyStore({ <<< error in this line.
      user: { me: null },
    });
    

    Literally that's all my code, if I do not export it, all works fine and I can use TanStack Query without problems.

    What am I doing wrong?

    bug 
    opened by InNickF 10
  • Unable to use contextQueries when key contains 2 variables

    Unable to use contextQueries when key contains 2 variables

    Maybe I dont get something but this code produces this error: Type '[string, string]' is not assignable to type 'readonly [ValidValue]'. Source has 2 element(s) but target allows only 1.

    export const keys = createQueryKeys('user', {
      byId: (accountId: string, userId: string) => ({
        queryKey: [accountId, userId],
        contextQueries: {
          likes: null,
        },
      }),
    });
    
    question 
    opened by Kamahl19 9
  • Typescript issues following example docs

    Typescript issues following example docs

    I'm getting some typescript errors following the examples in the readme and can't quite figure out what they mean or why they are happening.

    I copied exactly the todosKeys example and am getting the following errors related to specific lines

    single: (id: string) => id,
    
    Type '(id: string) => string' is not assignable to type 'FactoryProperty'.
    
    Type '(id: string) => string' is not assignable to type 'FactoryCallback'.
    
    Type 'string' is not assignable to type 'AnyObject'.
    
    Type '(id: string) => string' is not assignable to type 'FactoryProperty'.
      Type '(id: string) => string' is not assignable to type 'FactoryCallback'.
        Type 'string' is not assignable to type 'AnyObject'.ts(2322)
    QueryKeys.ts(11, 11): Did you mean to call this expression?
    

    One of the other ones is this line

    search: (query: string, limit: number) => [query, { limit }],
    
    Type '(query: string, limit: number) => (string | { limit: number; })[]' is not assignable to type 'FactoryProperty'.
    
    
    Type '(query: string, limit: number) => (string | { limit: number; })[]' is not assignable to type 'FactoryCallback'.
    
    Type '(string | { limit: number; })[]' is not assignable to type 'AnyObject'.
    
    Index signature for type 'string' is missing in type '(string | { limit: number; })[]'.
    
    (property) search: (query: string, limit: number) => (string | {
        limit: number;
    })[]
    
    Type '(query: string, limit: number) => (string | { limit: number; })[]' is not assignable to type 'FactoryProperty'.
      Type '(query: string, limit: number) => (string | { limit: number; })[]' is not assignable to type 'FactoryCallback'.
        Type '(string | { limit: number; })[]' is not assignable to type 'AnyObject'.
          Index signature for type 'string' is missing in type '(string | { limit: number; })[]'.ts(2322)
    

    I love the idea behind your library and would love to figure out why these are happening. I tried upgrading my typescript version to the latest and updating some of the properties in my tsconfig to match the projects but still receive these errors

    bug 
    opened by jrdn91 8
  • More ways of structure keys

    More ways of structure keys

    It would be nice to have a third level in the factory. For example if you have 5 different api urls with 30 endpoints underneed and not only a single one

    export const cmsKeys = createQueryKeys('cms', {
      content: {
        page: (locale: string, path: string) => [{ locale, path }],
        breadcrumb: (locale: string, path: string) => [{ locale, path }],
      }
    });
    

    So i can have a query like ['cms', 'content', 'page', { locale, path }].

    Basically i would have multiple swagger interfaces like this one. So in this case i would like ['bff', 'basket', 'baskets', { basketReference }] image

    enhancement 
    opened by TommySorensen 6
  • Help with nested queries (contextQueries) missing _ctx

    Help with nested queries (contextQueries) missing _ctx

    Hi, I am trying to create some context queries but I get an error when I try to access the _ctx. So what am I missing here: https://codesandbox.io/s/tannerlinsley-react-query-basic-typescript-forked-query-keys-factory-hqdnjg?file=/src/queries.ts ?

    import { createQueryKeyStore } from "@lukemorales/query-key-factory";
    import { useQuery, UseQueryResult } from "@tanstack/react-query";
    import { getPostById, getPostComments, getPosts } from "./api";
    import { Post } from "./types";
    
    export const queries = createQueryKeyStore({
      posts: {
        all: null,
        list: {
          queryKey: null,
          queryFn: () => getPosts()
        },
        item: (postId: number) => ({
          queryKey: [postId],
          queryFn: () => getPostById(postId),
    
          contextQueries: {
            comments: {
              queryKey: null,
              queryFn: () => getPostComments(postId)
            }
          }
        })
      }
    });
    console.log(queries.posts.item(1)); // Does not contain _ctx
    
    export function usePosts(): UseQueryResult<Post[]> {
      return useQuery(queries.posts.list);
    }
    
    export function usePost(postId: number): UseQueryResult<Post> {
      return useQuery(queries.posts.item(postId));
    }
    
    export function usePostComments(postId: number): UseQueryResult<string[]> {
      return useQuery(queries.posts.item(postId)._ctx.comments);
    }
    
    bug 
    opened by andersHj 4
  • Type QueryClient setQueryData

    Type QueryClient setQueryData

    (Copied from Discussions because it seems to have gone unnoticed there.)

    Hi,

    first off, thanks for the great package! I've been wanting to have something like this ever since I started using React Query over a year ago.

    Open here for some personal backstory why I love this package

    My main problem with React Query is that for queries there should be a single source of truth that links query key, query function, query parameter types and query return types together. But the useQuery hook makes you to define these relationships anew over and over again every time you call the hook, which wastes time, makes it easy to introduce an inconsistency, and makes it hard to refactor.

    So to solve this for the projects at work I had basically written my own "wrapper" around React Query which is very similar to what this package is doing, but since working with complex TypeScript type definitions can be quite daunting I didn't get as far as you guys. Which is why I was happy to finally discover this package! All this time I couldn't believe nobody else had the same problem with React Query as I did and created a package for it.

    I'm not sure if you're familiar, but Redux Toolkit has a library similar to React Query called RTK Query, and I much prefer their structure: You first define your API using the createApi function, where you define all the keys, functions, and data types. This was my main inspiration, but RTK Query goes a bit "too far" by (1) being centered around URLs and endpoints (not flexible for other types of APIs) and (2) requiring Redux which isn't a dependency necessary for every project. So React Query + a helper library is my preferred solution.


    The feature I propose is to enable typing of the QueryClient's setQueryData function. Through the queryFn field defined on the queries in the createQueryKeyStore function, we already give the type information of the data that this query will hold. Based on that we could type the setQueryFunction to accept only this type.

    The advantage is big: Currently, you can set the query data to any data you like, and then the type promise that useQuery() gives your components no longer holds true. It is up to the developer to keep track which query keys should contain what data types, and pass the right data every time. There are no type checks.

    Example:

    import { createQueryKeyStore } from "@lukemorales/query-key-factory";
    import { useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
    
    type User = {
        name: string;
    };
    
    const api = {
        getUser: (userId: string): User => ({
            name: "John",
        }),
    };
    
    export const queries = createQueryKeyStore({
      users: {
        all: null,
        detail: (userId: string) => ({
          queryKey: [userId],
          queryFn: () => api.getUser(userId),
        }),
      },
    });
    
    export const App = () => {
        const query = useQuery(queries.users.detail('14'));
        if (query.isSuccess) {
          // `query.data` is of type `User`, so it knows what type this query holds
          // we can safely access `query.data.name` and it will work
        }
    
        const queryClient = useQueryClient();
        queryClient.setQueryData(queries.users.detail('14').queryKey, () => ({
            // we can just pass any data here, including an object that contains no `name`
            // which will break the code above trying to access `name`
            email: "[email protected]",
        }));
    }
    

    And other functions such as getQueryData should also be type-safe.

    Use cases include Update from Mutation Responses and Optimistic Updates.

    As for the technical realization, either we could override React Query's type for QueryClient to be type safe using Module Augmentation, or by exporting our own TypedQueryClient and useTypedQueryClient() hooks are just wrappers around the original but with the proper type.

    enhancement good first issue 
    opened by Lesik 3
  • Issues with React Query Devtools

    Issues with React Query Devtools

    Hey ๐Ÿ‘‹ , first of all, thank you for creating this awesome lib ๐Ÿ™ I have problems using it with @tanstack/react-query-devtools.

    Here is an example from stack blitz, where you can reproduce the error: https://stackblitz.com/edit/nextjs-nsf8wi?file=pages/index.js

    Steps to reproduce the error:

    1. Open query dev tools in the UI
    2. Click on the stringKey query -> all works fine as this query key was not created with this lib
    3. Click on the queryFactoryKey query -> whole UI breaks with throw error TypeError: 'get' on proxy: property 'prototype' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '[object Array]' but got '[object Array]')

    For me (and probably many more devs) React Query Devtools are essential when using React Query, so this error is blocking me from using this awesome lib ๐Ÿ˜ž

    bug 
    opened by JosipVuk 3
  • Create inferQueryFactory type

    Create inferQueryFactory type

    First, nice work with this library!

    I think this library could provide a little helper to get the type of the queryKey passed to the queryFn, do you think that makes sense?

    The helper would look like this:

    import { useQuery, QueryFunctionContext } from '@tanstack/react-query'
    import { createQueryKeys, inferQueryFactory } from "@lukemorales/query-key-factory"
    
    export const productsKeys = createQueryKeys('products', {
      bestSelling: null,
      search: (query: string, limit = 15) => ({ query, limit }),
      byId: (productId: string) => ({ productId }),
    });
    
    type ProductKeys = inferQueryFactory<typeof productsKeys>
    
    const fetchTodos = (context: QueryFunctionContext<ProductKeys['byId']>) => {
      const queryKey = context.queryKey // readonly ['products', 'byId', { productId: string }]
    
      // fetch todos...
    }
    
    useQuery(productsKeys.byId('product-id'), fetchTodos)
    
    enhancement 
    opened by joaom00 3
  • Replace null with empty array []

    Replace null with empty array []

    Many projects use sindresorhus's eslint-plugin-unicorn. It's recommended config includes the no-null rule. Here is the reasoning behind not using null 1, 2, 3.

    I suppose it would make sense to replace it with an empty array [].

    question 
    opened by Kamahl19 2
  • Usage with useInfiniteQuery

    Usage with useInfiniteQuery

    Hi,

    Awesome library, been doing a lot of refactoring using this package...

    Is it possible to use this along with the useInfiniteQuery hook? Haven't been able to find any examples to do so.

    Thanks!

    question 
    opened by rodgetech 2
  • Allow computed props in keys #Vue

    Allow computed props in keys #Vue

    As a Vue user it would be really nice if I could use Computed props a key names See this example from tanstack query website ->

    // Get the user
    const { data: user } = useQuery({
      queryKey: ['user', email],
      queryFn: () => getUserByEmail(email.value),
    })
    
    const userId = computed(() => user.value?.id)
    const enabled = computed(() => !!user.value?.id)
    
    // Then get the user's projects
    const { isIdle, data: projects } = useQuery({
      queryKey: ['projects', userId],
      queryFn: () => getProjectsByUser(userId.value),
      enabled, // The query will not execute until `enabled == true`
    })
    

    Currently I get an type error when I translate this example to the query key factory. https://tanstack.com/query/v4/docs/vue/guides/dependent-queries

    opened by AlmarAubel 0
  • Allow typing the query type and Error when using useQuery

    Allow typing the query type and Error when using useQuery

    Taking this from the example:

    export const users = createQueryKeys('users', {
      list: () => ({
        queryKey: [],
        queryFn: () => api.findAll(),
      }),
    });
    
    export function useUserList() {
      return useQuery(users.list);
    };
    

    When doing:

    export function useUserDetail(id: string) {
      return useQuery<User[], Error>(users.list);
    };
    

    you get:

    "Type 'NonNullable<TQueryFnData> | never[]' is not assignable to type 'User[]'

    I'm afraid I'm having trouble using either codesandbox or stackblitz on my computer, so I can't provide a repro here :( I did consider providng a failing test, but you don't seem to test against useQuery directly in this repo according to the code search.

    The query does work just fine though, but I want to be able to type the second param as Error

    help wanted question 
    opened by jrobeson 4
  • defineQuery helper function

    defineQuery helper function

    As I start to use this library, I noticed the following pattern coming up quite a bit..

    const users = createQueryKeys('users', {
      byId: ({ id }) => ({
        queryKey: [{ id }],
        queryFn: () => api.getUser({id}),
      }),
      loggedIn: ({ id }) => ({
        queryKey: [{ id }],
        queryFn: () => api.getLoggedInUsers({id}),
      }),
      active: ({ id }) => ({
        queryKey: [{ id }],
        queryFn: () => api.getActiveUsers({id}),
      }),
    ...
    

    One way to avoid this is with a helper function that takes in a key, and defines that as the parameters for the query function (1-1 relationship). Something like this (defineQuery or querify):

    const defineQuery = (queryKey, queryFn) => ({
      queryKey: [queryKey],
      queryFn: () => queryFn(queryKey),
    });
    

    The end result is:

    const users = createQueryKeys('users', {
      byId: ({id}) => defineQuery({ id }, api.getUser),
      loggedIn: ({id}) => defineQuery({ id }, api.getLoggedInUsers),
      active: ({id}) => defineQuery({ id }, api.getActiveUsers),
    ...
    

    The nice thing about including this as a helper function is you don't have to force that particular style and users can opt-in as needed. Similar to #18.

    enhancement 
    opened by codebycarlos 3
  • Allow all query options within query definition (i.e. besides just queryKey, queryFn)

    Allow all query options within query definition (i.e. besides just queryKey, queryFn)

    What do you think about allowing all useQuery options, in order to allow more complex definitions?

    For instance, if we wanted to always run this detail query with refetchOnMount set to 'always':

      export const users = createQueryKeys('users', {
        detail: (userId: string) => ({
          queryKey: [userId],
          queryFn: () => api.getUser(userId),
        }),
      });
    

    to

      export const users = createQueryKeys('users', {
        detail: (userId: string) => ({
          queryKey: [userId],
          queryFn: () => api.getUser(userId),
          refetchOnMount: 'always', // add refetchOnMount set to 'always'
          ...more useQuery options here...
        }),
      });
    

    And so on..

    enhancement 
    opened by codebycarlos 2
  • feat(contextQueries): ierarhical contextQueries

    feat(contextQueries): ierarhical contextQueries

    before:

    export const queries = createQueryKeyStore({
      users: {
        all: null,
        detail: (userId: string) => ({
          queryKey: [userId],
          queryFn: () => api.getUser(userId),
        }),
      },
      todos: {
        detail: (todoId: string) => [todoId],
        list: (filters: TodoFilters) => ({
          queryKey: [{ filters }],
          queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
          contextQueries: {
            // here ->
            subTodos: ()=> ({ 
              queryKey: [] as unknown as [string],
              contextQueries: {
                search: (query: string, limit = 15) => ({
                  queryKey: [query, limit],
                  queryFn: (ctx) =>
                    api.getSearchTodos({
                      page: ctx.pageParam,
                      filters,
                      limit,
                      query,
                    }),
                }),
              },
            }),
          },
        }),
      },
    });
    queries.todos.list(filters)._ctx.subTodos()._ctx.search(query, limit)
    
    

    after:

    export const queries = createQueryKeyStore({
      users: {
        all: null,
        detail: (userId: string) => ({
          queryKey: [userId],
          queryFn: () => api.getUser(userId),
        }),
      },
      todos: {
        detail: (todoId: string) => [todoId],
        list: (filters: TodoFilters) => ({
          queryKey: [{ filters }],
          queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
          contextQueries: {
            // here ->
            subTodos: {
              contextQueries: {
                search: (query: string, limit = 15) => ({
                  queryKey: [query, limit],
                  queryFn: (ctx) =>
                    api.getSearchTodos({
                      page: ctx.pageParam,
                      filters,
                      limit,
                      query,
                    }),
                }),
              },
            },
          },
        }),
      },
    });
    queries.todos.list(filters)._ctx.subTodos._ctx.search(query, limit)
    
    
    opened by Naymi 3
  • refactor: replace internal types with `@tanstack/query-core` types

    refactor: replace internal types with `@tanstack/query-core` types

    Hello, I patched this locally, but figured I would also submit upstream.

    This PR replaces the types marked internal in the most recent release with the actual types from @tanstack/query-core.

    As of v1.0.3, the types marked internal are still being included in the build. When used in a project alongside TanStack Query, this duplication is causing TS to use the query-key-factory versions in place of (alongside?) the official TanStack types.

    This PR should fix those issues and ensure any future changes to TanStack Query's types stay in sync with this package.

    opened by wconnorwalsh 2
Releases(v1.0.3)
  • v1.0.3(Nov 8, 2022)

    @lukemorales/query-key-factory

    1.0.2

    Patch Changes

    1.0.1

    Patch Changes

    1.0.0

    Major Changes

    • #20 ba47907 Thanks @lukemorales! - ## Generate query options and add support for nested keys

      New in @lukemorales/query-key-factory is support for nested keys and generation of query options, adopting the query options overload as first class citizen, in preparation for React Query v5 roadmap.

      const people = createQueryKeys('people', {
        person: (id: number) => ({
          queryKey: [id],
          queryFn: () => api.getPerson({ params: { id } }),
          contextQueries: {
            ships: {
              queryKey: null,
              queryFn: () =>
                api.getShipsByPerson({
                  params: { personId: id },
                }),
            },
            film: (filmId: string) => ({
              queryKey: [filmId],
              queryFn: () =>
                api.getFilm({
                  params: { id: filmId },
                }),
            }),
          },
        }),
      });
      

      Each entry outputs an object that can be used in the query options overload in React Query:

      console.log(people.person('person_01'));
      
      // => output:
      // {
      //   queryKey: ['people', 'person', 'person_01'],
      //   queryFn: () => api.getPerson({ params: { id: 'person_01' } }),
      //   _ctx: { ...queries declared inside "contextQueries" }
      // }
      

      Then you can easily just use the object in useQuery or spread it and add more query options to that observer:

      export const Person = ({ id }) => {
        const personQuery = useQuery(people.person(id));
      
        return {
          /* render person data */
        };
      };
      
      export const Ships = ({ personId }) => {
        const shipsQuery = useQuery({
          ...people.person(personId)._ctx.ships,
          enabled: !!personId,
        });
      
        return {
          /* render ships data */
        };
      };
      
      export const Film = ({ id, personId }) => {
        const filmQuery = useQuery(people.person(personId)._ctx.film(id));
      
        return {
          /* render film data */
        };
      };
      

      BREAKING CHANGES

      Standardized query key values

      All query key values should now be an array. Only the first level keys (those not dynamically generated) can still be declared as null, but if you want to pass a value, you will need to make it an array.

      export const todosKeys = createQueryKeys('todos', {
        mine: null,
      - all: 'all-todos',
      + all: ['all-todos'],
      - list: (filters: TodoFilters) => ({ filters }),
      + list: (filters: TodoFilters) => [{ filters }],
      - todo: (todoId: string) => todoId,
      + todo: (todoId: string) => [todoId],
      });
      

      Objects are now used to declare query options

      You can still use objects to compose a query key, but now they must be inside an array because plain objects are now used for the declaration of the query options:

      export const todosKeys = createQueryKeys('todos', {
      - list: (filters: TodoFilters) => ({ filters }),
      + list: (filters: TodoFilters) => ({
      +   queryKey: [{ filters }],
      + }),
      });
      

      Generated output for a query key is always an object

      With the new API, the output of an entry will always be an object according to what options you've declared in the factory (e.g.: if you returned an array or declared an object with only queryKey, your output will be { queryKey: [...values] }, if you also declared queryFn it will be added to that object, and contextQueries will be available inside _ctx):

      export const todosKeys = createQueryKeys('todos', {
        todo: (todoId: string) => [todoId],
        list: (filters: TodoFilters) => {
          queryKey: [{ filters }],
          queryFn: () => fetchTodosList(filters),
        },
      });
      
      - useQuery(todosKeys.todo(todoId), fetchTodo);
      + useQuery(todosKeys.todo(todoId).queryKey, fetchTodo);
      
      - useQuery(todosKeys.list(filters), fetchTodosList);
      + useQuery(todosKeys.list(filters).queryKey, todosKeys.list(filters).queryFn);
      
      // even better refactor, preparing for React Query v5
      + useQuery({
      +   ...todosKeys.todo(todoId),
      +   queryFn: fetchTodo,
      + });
      
      + useQuery(todosKeys.list(filters));
      

      Helper types to infer query keys in the store reflect the new output

      The helper types to infer query keys in the created store reflect the new output, to account for all possible use cases:

      type TodosKeys = inferQueryKeys<typeof todosKeys>;
      
      - type SingleTodoQueryKey = TodosKeys['todo'];
      + type SingleTodoQueryKey = TodosKeys['todo']['queryKey'];
      
    • #20 ba47907 Thanks @lukemorales! - ## Remove deprecated methods Since v0.6.0, the default key and and toScope method have been deprecated from the package.

      BREAKING CHANGES

      default and toScope removed from implementation

      default key and toScope method have been officially removed from the code, which means that if you try to access them, you will either receive undefined or your code will throw for trying to invoke a function on toScope that does not exist anymore.

    0.6.1

    Patch Changes

    • Fix query key breaking devtools because of Proxy

    0.6.0

    Minor Changes

    • Introduce internal _def key and deprecate default and toScope

    0.5.0

    Minor Changes

    • Create createQueryKeyStore to allow declaring all feature keys in one place

    0.4.0

    Minor Changes

    • Create mergeQueryKeys to create single access point to all query keys

    0.3.2

    Patch Changes

    • Add inferQueryKeys type to create interface from result of createQueryKeys

    0.3.1

    Patch Changes

    • Fix new types not published

    0.3.0

    Minor Changes

    • Allow tuples of any size in dynamic keys

    0.2.1

    Patch Changes

    • Removes yarn engine restriction

    0.2.0

    Minor Changes

    • Make serializable keys less strict
    Source code(tar.gz)
    Source code(zip)
  • v1.0.2(Nov 8, 2022)

  • v1.0.1(Nov 7, 2022)

  • v1.0.0(Oct 24, 2022)

    Major Release

    • #20 ba47907 Thanks @lukemorales! - ## Generate query options and add support for nested keys

      New in @lukemorales/query-key-factory is support for nested keys and generation of query options, adopting the query options overload as first class citizen, in preparation for React Query v5 roadmap.

      const people = createQueryKeys('people', {
        person: (id: number) => ({
          queryKey: [id],
          queryFn: () => api.getPerson({ params: { id } }),
          contextQueries: {
            ships: {
              queryKey: null,
              queryFn: () =>
                api.getShipsByPerson({
                  params: { personId: id },
                }),
            },
            film: (filmId: string) => ({
              queryKey: [filmId],
              queryFn: () =>
                api.getFilm({
                  params: { id: filmId },
                }),
            }),
          },
        }),
      });
      

      Each entry outputs an object that can be used in the query options overload in React Query:

      console.log(people.person('person_01'));
      
      // => output:
      // {
      //   queryKey: ['people', 'person', 'person_01'],
      //   queryFn: () => api.getPerson({ params: { id: 'person_01' } }),
      //   _ctx: { ...queries declared inside "contextQueries" }
      // }
      

      Then you can easily just use the object in useQuery or spread it and add more query options to that observer:

      export const Person = ({ id }) => {
        const personQuery = useQuery(people.person(id));
      
        return {
          /* render person data */
        };
      };
      
      export const Ships = ({ personId }) => {
        const shipsQuery = useQuery({
          ...people.person(personId)._ctx.ships,
          enabled: !!personId,
        });
      
        return {
          /* render ships data */
        };
      };
      
      export const Film = ({ id, personId }) => {
        const filmQuery = useQuery(people.person(personId)._ctx.film(id));
      
        return {
          /* render film data */
        };
      };
      

      BREAKING CHANGES

      Standardized query key values

      All query key values should now be an array. Only the first level keys (those not dynamically generated) can still be declared as null, but if you want to pass a value, you will need to make it an array.

      export const todosKeys = createQueryKeys('todos', {
        mine: null,
      - all: 'all-todos',
      + all: ['all-todos'],
      - list: (filters: TodoFilters) => ({ filters }),
      + list: (filters: TodoFilters) => [{ filters }],
      - todo: (todoId: string) => todoId,
      + todo: (todoId: string) => [todoId],
      });
      

      Objects are now used to declare query options

      You can still use objects to compose a query key, but now they must be inside an array because plain objects are now used for the declaration of the query options:

      export const todosKeys = createQueryKeys('todos', {
      - list: (filters: TodoFilters) => ({ filters }),
      + list: (filters: TodoFilters) => ({
      +   queryKey: [{ filters }],
      + }),
      });
      

      Generated output for a query key is always an object

      With the new API, the output of an entry will always be an object according to what options you've declared in the factory (e.g.: if you returned an array or declared an object with only queryKey, your output will be { queryKey: [...values] }, if you also declared queryFn it will be added to that object, and contextQueries will be available inside _ctx):

      export const todosKeys = createQueryKeys('todos', {
        todo: (todoId: string) => [todoId],
        list: (filters: TodoFilters) => {
          queryKey: [{ filters }],
          queryFn: () => fetchTodosList(filters),
        },
      });
      
      - useQuery(todosKeys.todo(todoId), fetchTodo);
      + useQuery(todosKeys.todo(todoId).queryKey, fetchTodo);
      
      - useQuery(todosKeys.list(filters), fetchTodosList);
      + useQuery(todosKeys.list(filters).queryKey, todosKeys.list(filters).queryFn);
      
      // even better refactor, preparing for React Query v5
      + useQuery({
      +   ...todosKeys.todo(todoId),
      +   queryFn: fetchTodo,
      + });
      
      + useQuery(todosKeys.list(filters));
      

      Helper types to infer query keys in the store reflect the new output

      The helper types to infer query keys in the created store reflect the new output, to account for all possible use cases:

      type TodosKeys = inferQueryKeys<typeof todosKeys>;
      
      - type SingleTodoQueryKey = TodosKeys['todo'];
      + type SingleTodoQueryKey = TodosKeys['todo']['queryKey'];
      
    • #20 ba47907 Thanks @lukemorales! - ## Remove deprecated methods Since v0.6.0, the default key and and toScope method have been deprecated from the package.

      BREAKING CHANGES

      default and toScope removed from implementation

      default key and toScope method have been officially removed from the code, which means that if you try to access them, you will either receive undefined or your code will throw for trying to invoke a function on toScope that does not exist anymore.

    What's Changed

    • fix: remove arrays from being proxied by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/16
    • feat!: generate query options and support nested keys by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/20
    • docs: fix typo in README by @jayhawk24 in https://github.com/lukemorales/query-key-factory/pull/22
    • feat: allow declaring query options with only queryKey by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/23
    • feat: update infer type helpers to work with new API by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/24
    • chore(changesets): bump package version by @github-actions in https://github.com/lukemorales/query-key-factory/pull/21

    New Contributors

    • @jayhawk24 made their first contribution in https://github.com/lukemorales/query-key-factory/pull/22
    • @github-actions made their first contribution in https://github.com/lukemorales/query-key-factory/pull/21

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.6.0...v1.0.0

    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(Sep 16, 2022)

    Changes

    • fix: remove arrays from being proxied by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/16

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.6.0...v0.6.1

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Sep 11, 2022)

    Changes

    • refactor: introduce _def and deprecate default and toScope by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/14

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.5.0...v0.6.0

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Sep 5, 2022)

    Changes

    • feat: create `createQueryKeyStore` by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/12

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.4.0...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Sep 4, 2022)

    Changes

    • feat: create mergeQueryKeys util by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/11

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.3.2...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.2(Aug 30, 2022)

    Changes

    • feat: create inferQueryKeys type by @joaom00 in https://github.com/lukemorales/query-key-factory/pull/9

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.3.1...v0.3.2

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Aug 30, 2022)

    Changes

    • fix: repaired new factory types published on v0.3.0 (resolves #10)
    • docs: update example usage in README by @Shajansheriff in https://github.com/lukemorales/query-key-factory/pull/7

    New Contributors

    • @Shajansheriff made their first contribution in https://github.com/lukemorales/query-key-factory/pull/7

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.3.0...v0.3.1

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Aug 29, 2022)

    Changes

    • feat: allow tuples of any size in dynamic keys by @lukemorales in https://github.com/lukemorales/query-key-factory/pull/5

    Full Changelog: https://github.com/lukemorales/query-key-factory/compare/v0.2.1...v0.3.0

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Aug 29, 2022)

  • v0.2.0(Aug 28, 2022)

  • v0.1.0(Aug 28, 2022)

    First release of @lukemorales/query-key-factory.

    Changes

    Feat

    • Make createQueryKeys function
    • Throw Typescript error if factory schema contains default key
    Source code(tar.gz)
    Source code(zip)
Nuxt-Module, that provides a system to set shopware cache-tags for later use in e.g. a full-page cache

nuxt-shopware-caching Nuxt-Module, that provides a system to set shopware cache-tags for later use in e.g. a full-page cache. This module is meant to

Mothership GmbH 5 Nov 8, 2022
End-to-end typesafe APIs with tRPC.io in Nuxt applications.

tRPC-Nuxt End-to-end typesafe APIs with tRPC.io in Nuxt applications. The client above is not importing any code from the server, only its type declar

Robert Soriano 231 Dec 30, 2022
This repo contains a fully configured nuxt 3 instance supporting TypeScript and several considered as useful libraries, fully configured and ready to use in real world projects!

Nuxt 3 Starter This repo contains a fully configured nuxt 3 instance supporting TypeScript and several considered as useful libraries, fully configure

Ali Soueidan 26 Dec 27, 2022
Using Cypress with Vite, React, TypeScript, MSW and react-query

Vie + Cypress + MSW + react-query Demo Example of using Cypress with Vite, MSW and react-query. Uses the appReady pattern to signal to Cypress when th

Rob Caldecott 9 Jul 16, 2022
Nuxt 3 module for Kirby's Query Language API

nuxt-kql Kirby KQL module for Nuxt 3. This module provides a useKql composable, which under the hood uses useFetch. Thus, KQL query fetching in your N

Johann Schopplich 25 Dec 15, 2022
๐Ÿ—ƒ๏ธ Centralized State Management for Vue.js.

Vuex ?? HEADS UP! You're currently looking at Vuex 3 branch. If you're looking for Vuex 4, please check out 4.0 branch. Vuex is a state management pat

vuejs 27.9k Dec 30, 2022
Carpatin is a React JS Admin Dashboard Template that focuses on the management flows of a back-office application. We leverage the Material-UI power of stylizing the components in a way that feels more professional.

Carpatin Dashboard Free Carpatin is a React Js Admin Dashboard Template that focuses on the management flows of a back-office application. We leverage

Devias 64 Dec 12, 2022
A svelte action for creating tippy.js tooltips with full typescript support

Tippy.js for svelte A svelte action for creating tippy.js tooltips. Getting started # Pnpm pnpm add svelte-tippy tippy.js # Yarn yarn add svelte-tipp

Brett Mandler 11 Jul 19, 2022
๐ŸŒŠ A flexible and fun JavaScript file upload library

A JavaScript library that can upload anything you throw at it, optimizes images for faster uploads, and offers a great, accessible, silky smooth user

pqina 13.2k Jan 7, 2023
๐ŸŽ‰ A Vue.js 3.0 UI Library made by Element team

Element Plus - A Vue.js 3.0 UI library ?? Vue 3.0 Composition API ?? Written in TypeScript Status: Beta This project is still under heavy development.

null 18.3k Jan 9, 2023
A lightweight Vue.js UI library with a simple API, inspired by Google's Material Design.

Keen UI Keen UI is a Vue.js UI library with a simple API, inspired by Google's Material Design. Keen UI is not a CSS framework. Therefore, it doesn't

Josephus Paye II 4.1k Jan 2, 2023
JavaScript Survey and Form Library

SurveyJS is a JavaScript Survey and Form Library. SurveyJS is a modern way to add surveys and forms to your website. It has versions for Angular, jQue

SurveyJS 3.5k Jan 5, 2023
The first truly composable CSS animation library. Built for Vue, React, SCSS, and CSS, AnimXYZ will bring your website to life.

AnimXYZ animxyz.com AnimXYZ helps you create, customize, and compose animations for your website. Powered by CSS variables to allow a nearly limitless

Ingram Projects 2.1k Jan 2, 2023
Jenesius vue modal is simple library for Vue 3 only

Jenesius Vue Modal Jenesius vue modal is simple library for Vue 3 only . Site Documentation Installation npm i jenesius-vue-modal For add modals in yo

ะั€ั…ะธะฟั†ะตะฒ ะ•ะฒะณะตะฝะธะน 63 Dec 30, 2022
Mosha-vue-toastify - A light weight and fun Vue 3 toast or notification or snack bar or however you wanna call it library.

Mosha Vue Toastify A lightweight and fun Vue 3 toast or notification or snack bar or however you wanna call it library. English | ็ฎ€ไฝ“ไธญๆ–‡ Talk is cheap,

Baidi Liu 187 Jan 2, 2023
Small library to accomplish common tasks in Svelte/SvelteKit

svelte-utilities svelte-utilities is a small utilities library for Svelte/SvelteKit that provides features I use in almost every project, such as: Laz

null 40 Nov 25, 2022
๐Ÿ Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all โ€” including browsers?

Open Web Labs 14 Dec 24, 2022
๐Ÿ Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all โ€” including browsers?

Open Web Labs 5 May 1, 2022
Frolics is an offline, lightweight, full-text search library for Frontend applications

Frolics What Is Frolics Installation Document Preparing Customized Fields Hand-on Example Cache Usage What Is Frolics ? Frolics is an offline, lightwe

null 13 Dec 4, 2022