TanStack DB
A TanStack DB query collection over a procedure's queryOptions(), with optimistic inserts that call mutations.
A query collection takes a query key and function, so queryOptions() plugs in without an adapter. Rows keep the serializer's types (a Date stays a Date), and the collection's handlers call mutations:
pnpm add @tanstack/db @tanstack/query-db-collectionnpm install @tanstack/db @tanstack/query-db-collectionyarn add @tanstack/db @tanstack/query-db-collectionbun add @tanstack/db @tanstack/query-db-collectionimport { function createCollection<T extends StandardSchemaV1, TKey extends string | number, TUtils extends UtilsRecord>(options: Omit<CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils>, `utils`> & {
schema: T;
utils: TUtils;
} & NonSingleResult): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> & NonSingleResult (+7 overloads)
createCollection } from '@tanstack/db';
import { function queryCollectionOptions<T extends StandardSchemaV1, TQueryFn extends (context: QueryFunctionContext<any>) => any, TError = unknown, TQueryKey extends QueryKey = readonly unknown[], TKey extends string | number = string | number, TQueryData = Awaited<ReturnType<TQueryFn>>>(config: Omit<QueryCollectionConfig<InferSchemaOutput<T>, TQueryFn, TError, TQueryKey, TKey, T, TQueryData>, `select`> & {
schema: T;
select: (data: TQueryData) => Array<InferSchemaOutput<T>>;
}): CollectionConfig<InferSchemaOutput<...>, TKey, T, QueryCollectionUtils<InferSchemaOutput<...>, TKey, InferSchemaInput<...>, TError>> & {
schema: T;
utils: QueryCollectionUtils<InferSchemaOutput<...>, TKey, InferSchemaInput<...>, TError>;
} (+3 overloads)
queryCollectionOptions } from '@tanstack/query-db-collection';
const const list: QueryOptionsOut<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>
list = const trpc: TrpcQueryUtils<TrpcClient<{
post: {
list: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
limit: z.ZodNumber;
cursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>, "query", {
output: {
items: Post[];
nextCursor: string | undefined;
};
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>>
trpc.post: TrpcQueryUtils<ClientRouter<{
list: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
limit: z.ZodNumber;
cursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>, "query", {
output: {
items: Post[];
nextCursor: string | undefined;
};
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>>
post.list: QueryProcedureUtils<{
limit: number;
cursor?: string | undefined;
}, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>, Omit<...>>
list.QueryProcedureUtils<{ limit: number; cursor?: string | undefined; }, { items: { id: string; title: string; author: string | null; createdAt: string; }[]; nextCursor?: string | undefined; }, NonNullable<UnexpectedError | (TrpcError<...> & { ...; })>, Omit<...>>.queryOptions<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>(opts: {
input: {
limit: number;
cursor?: string | undefined;
} | unique symbol;
} & ContextBag<Omit<NonNullable<{
signal?: AbortSignal | undefined;
} & BuiltinCallOptions & {}>, "signal" | "lastEventId">> & QueryOptionsIn<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<...>> & {
...;
})>, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>): QueryOptionsOut<...> (+1 overload)
queryOptions({ input: {
limit: number;
cursor?: string | undefined;
} | unique symbol
input: { limit: numberlimit: 50 } });
const const posts: Collection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, never, {
id: string;
title: string;
author: string | null;
createdAt: string;
}> & NonSingleResult
posts = createCollection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>>(options: Omit<CollectionConfig<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, never, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>>, "utils"> & {
...;
} & NonSingleResult): Collection<...> & NonSingleResult (+7 overloads)
createCollection(
queryCollectionOptions<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, QueryFunction<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 10 more ... | {
...;
}>> & {
...;
})>;
}>, unknown, TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 10 more ... | {
...;
}>> & {
...;
})>;
}, string, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>(config: QueryCollectionConfig<...> & {
...;
}): CollectionConfig<...> & {
...;
} (+3 overloads)
queryCollectionOptions({
QueryCollectionConfig<{ id: string; title: string; author: string | null; createdAt: string; }, QueryFunction<{ items: { id: string; title: string; author: string | null; createdAt: string; }[]; nextCursor?: string | undefined; }, TrpcQueryKey & { ...; }>, ... 4 more ..., { items: { id: string; title: string; author: string | null; createdAt: string; }[]; nextCursor?: string | undefined; }>.queryKey: (TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>;
}) | TQueryKeyBuilder<...>
queryKey: const list: QueryOptionsOut<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>
list.queryKey: TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>;
}
queryKey,
QueryCollectionConfig<{ id: string; title: string; author: string | null; createdAt: string; }, QueryFunction<{ items: { id: string; title: string; author: string | null; createdAt: string; }[]; nextCursor?: string | undefined; }, TrpcQueryKey & { ...; }>, ... 4 more ..., { items: { id: string; title: string; author: string | null; createdAt: string; }[]; nextCursor?: string | undefined; }>.queryFn: QueryFunction<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>;
}>
queryFn: const list: QueryOptionsOut<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
readonly maximum: string | number;
readonly inclusive?: boolean | undefined;
readonly exact?: boolean | undefined;
readonly path: readonly [] | ... 1 more ... | readonly [...];
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>, {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}>
list.queryFn: QueryFunction<{
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}, TrpcQueryKey & {
[dataTagSymbol]: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
};
[dataTagErrorSymbol]: NonNullable<UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["limit"] | readonly ["cursor"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>> & {
...;
})>;
}>
queryFn,
select: ((data: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}) => {
id: string;
title: string;
author: string | null;
createdAt: string;
}[]) & ((data: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}) => {
id: string;
title: string;
author: string | null;
createdAt: string;
}[])
select: (data: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}
data) => data: {
items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[];
nextCursor?: string | undefined;
}
data.items: {
id: string;
title: string;
author: string | null;
createdAt: string;
}[]
items,
QueryCollectionConfig<T extends object = object, TQueryFn extends (context: QueryFunctionContext<any>) => any = (context: QueryFunctionContext<any>) => any, TError = unknown, TQueryKey extends QueryKey = readonly unknown[], TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never, TQueryData = Awaited<...>>.queryClient: QueryClientqueryClient,
BaseCollectionConfig<{ id: string; title: string; author: string | null; createdAt: string; }, string, never, QueryCollectionUtils<{ id: string; title: string; author: string | null; createdAt: string; }, string, { id: string; title: string; author: string | null; createdAt: string; }, unknown>, any>.getKey: (item: {
id: string;
title: string;
author: string | null;
createdAt: string;
}) => string
getKey: (post: {
id: string;
title: string;
author: string | null;
createdAt: string;
}
post) => post: {
id: string;
title: string;
author: string | null;
createdAt: string;
}
post.id: stringid,
BaseCollectionConfig<{ id: string; title: string; author: string | null; createdAt: string; }, string, never, QueryCollectionUtils<{ id: string; title: string; author: string | null; createdAt: string; }, string, { id: string; title: string; author: string | null; createdAt: string; }, unknown>, any>.onInsert?: InsertMutationFn<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, void> | InsertMutationFn<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, any> | undefined
onInsert: async (opts: InsertMutationFnParams<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>>
opts) => {
for (const const mutation: PendingMutation<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, "insert", Collection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, StandardSchemaV1<unknown, unknown>, {
id: string;
title: string;
author: string | null;
createdAt: string;
}>>
mutation of opts: InsertMutationFnParams<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>>
opts.transaction: TransactionWithMutations<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, "insert", Collection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, StandardSchemaV1<unknown, unknown>, {
id: string;
title: string;
author: string | null;
createdAt: string;
}>>
transaction.mutations: [PendingMutation<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, "insert", Collection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, StandardSchemaV1<unknown, unknown>, {
id: string;
title: string;
author: string | null;
createdAt: string;
}>>, ...PendingMutation<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, "insert", Collection<...>>[]]
mutations) {
await const client: TrpcClient<{
post: {
list: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
limit: z.ZodNumber;
cursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>, "query", {
output: {
items: Post[];
nextCursor: string | undefined;
};
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<...>;
};
}, {}, undefined, LinksTypes<...>>
client.post: ClientRouter<{
list: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
limit: z.ZodNumber;
cursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>, "query", {
output: {
items: Post[];
nextCursor: string | undefined;
};
errors: never;
services: never;
unmapped: never;
}>;
byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<...>;
}, {}, undefined, LinksTypes<...>>
post.create: {
mutate(input: {
title: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, CallError<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<"UNAUTHORIZED", undefined> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["title"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>;
}
create.function mutate(input: {
title: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, CallError<{
type: "mutation";
ctx: Context;
meta: {};
input: {
title: string;
};
output: Post;
errors: TrpcError<"UNAUTHORIZED", undefined> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: z.core.$ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["title"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>
mutate({ title: stringtitle: const mutation: PendingMutation<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, "insert", Collection<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, QueryCollectionUtils<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, string, {
id: string;
title: string;
author: string | null;
createdAt: string;
}, unknown>, StandardSchemaV1<unknown, unknown>, {
id: string;
title: string;
author: string | null;
createdAt: string;
}>>
mutation.PendingMutation<{ id: string; title: string; author: string | null; createdAt: string; }, "insert", Collection<{ id: string; title: string; author: string | null; createdAt: string; }, string, QueryCollectionUtils<{ id: string; title: string; author: string | null; createdAt: string; }, string, { id: string; title: string; author: string | null; createdAt: string; }, unknown>, StandardSchemaV1<...>, { id: string; title: string; author: string | null; createdAt: string; }>>.modified: {
id: string;
title: string;
author: string | null;
createdAt: string;
}
modified.title: stringtitle });
}
},
}),
);posts.insert(...) shows the row at once, and the collection refetches post.list once onInsert resolves. The query lives in the same QueryClient, so invalidateQueries with the tRPC key refreshes the collection too.