Query keys and filters
How tRPC's query keys are shaped, the filters that invalidate and read them, and keyPrefix for two APIs in one QueryClient.
On this page
A query key starts with the procedure's path as an array, then its input and what made it: [['post', 'byId'], { input: { id: '1' }, type: 'query' }]. The type is query, infinite, streamed or live, or mutation for a streamed mutation's chunks. A mutation key is the path alone, [['post', 'create']]. Since the path is an array, [['post']] partially matches every key under post.
You rarely write a key: each helper makes its own, and has a function that returns it.
| Helper | Key or filter for |
|---|---|
trpc.pathKey(), trpc.post.pathKey() | everything under a path: queries, infinite, streamed and live queries, mutations |
trpc.post.pathFilter(filters?) | the same, as filters for invalidateQueries, cancelQueries, removeQueries … |
trpc.post.byId.queryKey({ input }) | the exact key queryOptions() uses, typed for getQueryData |
trpc.post.byId.queryFilter({ input? }) | a procedure's queries, every input or one |
trpc.post.list.infiniteKey({ input, initialPageParam }) | the exact key infiniteOptions() uses |
trpc.post.create.mutationKey(), mutationFilter() | a procedure's mutations, for useIsMutating and useMutationState |
trpc.x.streamedKey(), trpc.x.liveKey() | a subscription's query-cache forms |
Invalidate and update
// every post query, whatever its procedure or input
await const queryClient: QueryClientqueryClient.QueryClient.invalidateQueries<TrpcQueryKey>(filters?: InvalidateQueryFilters<TrpcQueryKey> | undefined, options?: InvalidateOptions): Promise<void>invalidateQueries(const trpc: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: TrpcQueryUtils<ClientRouter<{
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
}, {}, undefined, LinksTypes<...>>>
post.PathUtils.pathFilter(filters?: QueryFiltersIn): QueryFiltersIn & {
queryKey: TrpcQueryKey;
}
pathFilter());
// post.byId for one input
await const queryClient: QueryClientqueryClient.QueryClient.invalidateQueries<TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}>(filters?: InvalidateQueryFilters<...> | undefined, options?: InvalidateOptions): Promise<void>
invalidateQueries(
const trpc: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: TrpcQueryUtils<ClientRouter<{
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
}, {}, undefined, LinksTypes<...>>>
post.byId: QueryProcedureUtils<{
id: string;
}, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | (TrpcError<"PAYMENT_REQUIRED", undefined> & {
readonly defined: false;
}) | ... 20 more ... | (TrpcError<...> & {
...;
})>, Omit<...>>
byId.QueryProcedureUtils<{ id: string; }, { id: string; title: string; author: string | null; createdAt: string; }, NonNullable<(TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 25 more ... | (TrpcError<...> & { ...; })>, Omit<...>>.queryFilter(opts?: ({
input?: {
id: string;
} | undefined;
} & QueryFiltersIn) | undefined): QueryFiltersIn & {
queryKey: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | ... 23 more ... | (TrpcError<...> & {
...;
})>;
};
}
queryFilter({ input?: {
id: string;
} | undefined
input: { id: stringid } }),
);
// typed by the key: the procedure's output
const const key: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}
key = const trpc: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: TrpcQueryUtils<ClientRouter<{
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
}, {}, undefined, LinksTypes<...>>>
post.byId: QueryProcedureUtils<{
id: string;
}, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | (TrpcError<"PAYMENT_REQUIRED", undefined> & {
readonly defined: false;
}) | ... 20 more ... | (TrpcError<...> & {
...;
})>, Omit<...>>
byId.QueryProcedureUtils<{ id: string; }, { id: string; title: string; author: string | null; createdAt: string; }, NonNullable<(TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 25 more ... | (TrpcError<...> & { ...; })>, Omit<...>>.queryKey(opts: {
input: {
id: string;
} | unique symbol;
}): TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | ... 22 more ... | (TrpcError<...> & {
...;
})>;
}
queryKey({ input: {
id: string;
} | unique symbol
input: { id: stringid } });
const post = const queryClient: QueryClientqueryClient.QueryClient.getQueryData<unknown, TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}, {
...;
}>(queryKey: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}): {
...;
} | undefined
getQueryData(const key: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}
key);const queryClient: QueryClientqueryClient.QueryClient.setQueryData<unknown, TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}, {
...;
}>(queryKey: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}, updater: Updater<...>, options?: SetDataOptions): NoInfer<{
...;
}> | undefined
setQueryData(const key: TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | ... 21 more ... | (TrpcError<...> & {
...;
})>;
}
key, (old: NoInfer<{
id: string;
title: string;
author: string | null;
createdAt: string;
}> | undefined
old) =>
old: NoInfer<{
id: string;
title: string;
author: string | null;
createdAt: string;
}> | undefined
old ? { ...old: NoInfer<{
id: string;
title: string;
author: string | null;
createdAt: string;
}>
old, title: stringtitle: 'Renamed' } : old: undefinedold,
);Two APIs, one QueryClient
Keys start with the procedure path, so two tRPC APIs with a post.byId would share cache entries. Give one a keyPrefix:
const const billing: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
billing = createTrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>(opts: CreateTrpcQueryUtilsOptions<...>): TrpcQueryUtils<...>
createTrpcQueryUtils({
CreateTrpcQueryUtilsOptions<TrpcClient<{ post: { byId: Procedure<{ type: "query"; ctx: Context; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; list: Procedure<...>; create: Procedure<...>; }; }, {}, undefined, LinksTypes<...>>>.client: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client: const billingClient: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
billingClient,
CreateTrpcQueryUtilsOptions<TClient>.keyPrefix?: string | undefinedkeyPrefix: 'billing',
});
const billing: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
billing.post: TrpcQueryUtils<ClientRouter<{
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
}, {}, undefined, LinksTypes<...>>>
post.byId: QueryProcedureUtils<{
id: string;
}, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
readonly defined: false;
}) | (TrpcError<"PAYMENT_REQUIRED", undefined> & {
readonly defined: false;
}) | ... 20 more ... | (TrpcError<...> & {
...;
})>, Omit<...>>
byId.QueryProcedureUtils<{ id: string; }, { id: string; title: string; author: string | null; createdAt: string; }, NonNullable<(TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 25 more ... | (TrpcError<...> & { ...; })>, Omit<...>>.queryKey(opts: {
input: {
id: string;
} | unique symbol;
}): TrpcQueryKey & {
[dataTagSymbol]: {
id: string;
title: string;
author: string | null;
createdAt: string;
};
[dataTagErrorSymbol]: NonNullable<(TrpcError<"NOT_FOUND", undefined> & {
readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
readonly defined: false;
}) | ... 22 more ... | (TrpcError<...> & {
...;
})>;
}
queryKey({ input: {
id: string;
} | unique symbol
input: { id: stringid } }); // [['billing', 'post', 'byId'], { input: { id }, type: 'query' }]
const billing: TrpcQueryUtils<TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
list: Procedure<{
type: "query";
ctx: Context;
meta: {};
input: undefined;
output: Post[];
errors: never;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<...> | SchemaValidationError<...>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>>
billing.PathUtils.pathFilter(filters?: QueryFiltersIn): QueryFiltersIn & {
queryKey: TrpcQueryKey;
}
pathFilter(); // matches only billing's queriesEvery framework's createTrpcContext({ keyPrefix: 'billing' }) and createTrpcRsc({ keyPrefix }) take it too; the server and browser must use the same one.
Next.js cache tags
cacheTags(key) and revalidationTag(key) turn a key into Next.js data-cache tags, so a mutation can revalidate exactly what a query key covers (React Server Components).