Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feat: Add transformer option to use-query #501

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion packages/postgrest-core/src/mutate-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const mutateOperation = <Type extends Record<string, unknown>>(
primaryKeys: (keyof Type)[],
filter: Pick<PostgrestFilter<Type>, 'apply'>,
orderBy?: OrderDefinition[],
transformer?: (v: Type) => Type,
) => {
// find item
const itemIdx = currentData.findIndex((oldItem) =>
Expand Down Expand Up @@ -52,7 +53,11 @@ export const mutateOperation = <Type extends Record<string, unknown>>(

// check that new item is still a valid member of the list and has all required paths
if (filter.apply(newItem)) {
currentData.splice(newItemIdx, 0, newItem);
currentData.splice(
newItemIdx,
0,
transformer ? transformer(newItem) : newItem,
);
}

return currentData;
Expand All @@ -64,6 +69,7 @@ export type MutateItemOperation<Type extends Record<string, unknown>> = {
input: Partial<Type>;
mutate: (current: Type) => Type;
primaryKeys: (keyof Type)[];
transformer?: (v: Type) => Type;
} & RevalidateOpts<Type>;

export type MutateItemCache<KeyType, Type extends Record<string, unknown>> = {
Expand Down Expand Up @@ -113,6 +119,7 @@ export const mutateItem = async <KeyType, Type extends Record<string, unknown>>(
schema,
table,
primaryKeys,
transformer,
} = op;
const { cacheKeys, decode, getPostgrestFilter, mutate, revalidate } = cache;

Expand Down Expand Up @@ -165,6 +172,7 @@ export const mutateItem = async <KeyType, Type extends Record<string, unknown>>(
primaryKeys,
filter,
orderBy,
transformer,
),
currentData,
limit,
Expand All @@ -178,6 +186,7 @@ export const mutateItem = async <KeyType, Type extends Record<string, unknown>>(
primaryKeys,
filter,
orderBy,
transformer,
),
limit,
);
Expand Down Expand Up @@ -207,6 +216,7 @@ export const mutateItem = async <KeyType, Type extends Record<string, unknown>>(
primaryKeys,
filter,
orderBy,
transformer,
);

return {
Expand Down
12 changes: 11 additions & 1 deletion packages/postgrest-core/src/upsert-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const upsert = <Type extends Record<string, unknown>>(
filter: Pick<PostgrestFilter<Type>, 'apply'>,
mergeFn?: MergeFn<Type>,
orderBy?: OrderDefinition[],
transformer?: (v: Type) => Type,
) => {
const merge = mergeFn ?? (mergeAnything as MergeFn<Type>);

Expand Down Expand Up @@ -59,7 +60,11 @@ export const upsert = <Type extends Record<string, unknown>>(

// check that new item is still a valid member of the list and has all required paths
if (filter.apply(newItem)) {
currentData.splice(newItemIdx, 0, newItem);
currentData.splice(
newItemIdx,
0,
transformer ? transformer(newItem) : newItem,
);
}

return currentData;
Expand All @@ -71,6 +76,7 @@ export type UpsertItemOperation<Type extends Record<string, unknown>> = {
input: Type;
primaryKeys: (keyof Type)[];
merge?: (current: Type, input: Type) => Type;
transformer?: (v: Type) => Type;
} & RevalidateOpts<Type>;

export type UpsertItemCache<KeyType, Type extends Record<string, unknown>> = {
Expand Down Expand Up @@ -118,6 +124,7 @@ export const upsertItem = async <KeyType, Type extends Record<string, unknown>>(
schema,
table,
primaryKeys,
transformer,
} = op;
const { cacheKeys, decode, getPostgrestFilter, mutate, revalidate } = cache;

Expand Down Expand Up @@ -167,6 +174,7 @@ export const upsertItem = async <KeyType, Type extends Record<string, unknown>>(
filter,
merge,
orderBy,
transformer,
),
currentData,
limit,
Expand All @@ -180,6 +188,7 @@ export const upsertItem = async <KeyType, Type extends Record<string, unknown>>(
filter,
merge,
orderBy,
transformer,
),
limit,
);
Expand Down Expand Up @@ -209,6 +218,7 @@ export const upsertItem = async <KeyType, Type extends Record<string, unknown>>(
filter,
merge,
orderBy,
transformer,
);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function useInsertMutation<
opts?: Omit<
UsePostgrestMutationOpts<S, T, RelationName, Re, 'Insert', Q, R>,
'mutationFn'
>,
> & { transformer?: (v: T['Row']) => Record<string, unknown> },
) {
const queriesForTable = useQueriesForTableLoader(getTable(qb));
const upsertItem = useUpsertItem({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function useUpdateMutation<
opts?: Omit<
UsePostgrestMutationOpts<S, T, RelationName, Re, 'UpdateOne', Q, R>,
'mutationFn'
>,
> & { transformer?: (v: T['Row']) => Record<string, unknown> },
) {
const queriesForTable = useQueriesForTableLoader(getTable(qb));
const upsertItem = useUpsertItem({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function useUpsertMutation<
opts?: Omit<
UsePostgrestMutationOpts<S, T, RelationName, Re, 'Upsert', Q, R>,
'mutationFn'
>,
> & { transformer?: (v: T['Row']) => Record<string, unknown> },
) {
const queriesForTable = useQueriesForTableLoader(getTable(qb));
const upsertItem = useUpsertItem({
Expand Down
27 changes: 22 additions & 5 deletions packages/postgrest-react-query/src/query/build-query-opts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@ import type { UseQueryOptions as UseReactQueryOptions } from '@tanstack/react-qu

import { encode } from '../lib/key';

export function buildQueryOpts<Result>(
export function buildQueryOpts<Result, TransformedResult = Result>(
query: PromiseLike<AnyPostgrestResponse<Result>>,
config?: Omit<
UseReactQueryOptions<AnyPostgrestResponse<Result>, PostgrestError>,
UseReactQueryOptions<
AnyPostgrestResponse<TransformedResult>,
PostgrestError
>,
'queryKey' | 'queryFn'
>,
): UseReactQueryOptions<AnyPostgrestResponse<Result>, PostgrestError> {
> & {
transformer?: (
data: AnyPostgrestResponse<Result>['data'],
) => TransformedResult;
},
): UseReactQueryOptions<
AnyPostgrestResponse<TransformedResult>,
PostgrestError
> {
return {
queryKey: encode<Result>(query, false),
queryFn: async ({ signal }) => {
Expand All @@ -24,7 +34,14 @@ export function buildQueryOpts<Result>(
if (isPostgrestBuilder(query)) {
query = query.throwOnError();
}
return await query;
const result = await query;
if (config?.transformer && result.error === null) {
return {
...result,
data: config.transformer(result.data),
};
}
return result as AnyPostgrestResponse<TransformedResult>;
},
...config,
};
Expand Down
77 changes: 51 additions & 26 deletions packages/postgrest-react-query/src/query/use-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,65 +76,90 @@ export type UseQueryAnyReturn<Result> = Omit<
* React hook to execute a PostgREST query and return a single item response.
*
* @param {PromiseLike<PostgrestSingleResponse<Result>>} query A promise that resolves to a PostgREST single item response.
* @param {Omit<UseReactQueryOptions<PostgrestSingleResponse<Result>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQuerySingleReturn<Result>} The hook result containing the single item response data.
* @param {Omit<UseReactQueryOptions<PostgrestSingleResponse<TransformedResult>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQuerySingleReturn<TransformedResult>} The hook result containing the single item response data.
*/
function useQuery<Result>(
function useQuery<Result, TransformedResult = Result>(
query: PromiseLike<PostgrestSingleResponse<Result>>,
config?: Omit<
UseReactQueryOptions<PostgrestSingleResponse<Result>, PostgrestError>,
UseReactQueryOptions<
PostgrestSingleResponse<TransformedResult>,
PostgrestError
>,
'queryKey' | 'queryFn'
>,
): UseQuerySingleReturn<Result>;
> & {
transformer?: (
data: PostgrestSingleResponse<Result>['data'],
) => TransformedResult;
},
): UseQuerySingleReturn<TransformedResult>;
/**
* React hook to execute a PostgREST query and return a maybe single item response.
*
* @param {PromiseLike<PostgrestMaybeSingleResponse<Result>>} query A promise that resolves to a PostgREST maybe single item response.
* @param {Omit<UseReactQueryOptions<PostgrestMaybeSingleResponse<Result>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryMaybeSingleReturn<Result>} The hook result containing the maybe single item response data.
* @param {Omit<UseReactQueryOptions<PostgrestMaybeSingleResponse<TransformedResult>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryMaybeSingleReturn<TransformedResult>} The hook result containing the maybe single item response data.
*/
function useQuery<Result>(
function useQuery<Result, TransformedResult = Result>(
query: PromiseLike<PostgrestMaybeSingleResponse<Result>>,
config?: Omit<
UseReactQueryOptions<PostgrestMaybeSingleResponse<Result>, PostgrestError>,
UseReactQueryOptions<
PostgrestMaybeSingleResponse<TransformedResult>,
PostgrestError
>,
'queryKey' | 'queryFn'
>,
): UseQueryMaybeSingleReturn<Result>;
> & {
transformer?: (
data: PostgrestMaybeSingleResponse<Result>['data'],
) => TransformedResult;
},
): UseQueryMaybeSingleReturn<TransformedResult>;
/**
* React hook to execute a PostgREST query.
*
* @template Result The expected response data type.
* @param {PromiseLike<PostgrestResponse<Result>>} query A promise that resolves to a PostgREST response.
* @param {Omit<UseReactQueryOptions<PostgrestResponse<Result>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryReturn<Result>} The hook result containing the response data.
* @param {Omit<UseReactQueryOptions<PostgrestResponse<TransformedResult>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryReturn<TransformedResult>} The hook result containing the response data.
*/
function useQuery<Result>(
function useQuery<Result, TransformedResult = Result>(
query: PromiseLike<PostgrestResponse<Result>>,
config?: Omit<
UseReactQueryOptions<PostgrestResponse<Result>, PostgrestError>,
UseReactQueryOptions<PostgrestResponse<TransformedResult>, PostgrestError>,
'queryKey' | 'queryFn'
>,
): UseQueryReturn<Result>;
> & {
transformer?: (
data: PostgrestResponse<Result>['data'],
) => TransformedResult;
},
): UseQueryReturn<TransformedResult>;

/**
* React hook to execute a PostgREST query.
*
* @template Result The expected response data type.
* @param {PromiseLike<AnyPostgrestResponse<Result>>} query A promise that resolves to a PostgREST response of any kind.
* @param {Omit<UseReactQueryOptions<AnyPostgrestResponse<Result>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryAnyReturn<Result>} The hook result containing the response data.
* @param {Omit<UseReactQueryOptions<AnyPostgrestResponse<TransformedResult>, PostgrestError>, 'queryKey' | 'queryFn'>} [config] The React Query options.
* @returns {UseQueryAnyReturn<TransformedResult>} The hook result containing the response data.
*/
function useQuery<Result>(
function useQuery<Result, TransformedResult = Result>(
query: PromiseLike<AnyPostgrestResponse<Result>>,
config?: Omit<
UseReactQueryOptions<AnyPostgrestResponse<Result>, PostgrestError>,
UseReactQueryOptions<
AnyPostgrestResponse<TransformedResult>,
PostgrestError
>,
'queryKey' | 'queryFn'
>,
): UseQueryAnyReturn<Result> {
> & {
transformer?: (
data: AnyPostgrestResponse<Result>['data'],
) => TransformedResult;
},
): UseQueryAnyReturn<TransformedResult> {
const { data, ...rest } = useReactQuery<
AnyPostgrestResponse<Result>,
AnyPostgrestResponse<TransformedResult>,
PostgrestError
>(buildQueryOpts<Result>(query, config));
>(buildQueryOpts<Result, TransformedResult>(query, config));

return { data: data?.data, count: data?.count ?? null, ...rest };
}
Expand Down