Files
edr-platform/apps/edr-freight-web/backoffice/src/utils/endpoint.ts
2026-06-04 16:50:38 +03:00

125 lines
3.1 KiB
TypeScript

import {
UseQueryOptions,
UseMutationOptions
} from "@tanstack/react-query";
// ---------------------------------------------------------------------------
// React Query shared types
// ---------------------------------------------------------------------------
export type QueryConfig<T> = Omit<
UseQueryOptions<T, Error, T, readonly unknown[]>,
"queryKey" | "queryFn"
>;
// ---------------------------------------------------------------------------
// Endpoint interfaces
// ---------------------------------------------------------------------------
export interface EndpointWithInput<TInput, TResponse> {
call(input: TInput): Promise<TResponse>;
queryKey(input: TInput): readonly unknown[];
queryOptions(
config: { input: TInput } & QueryConfig<TResponse>,
): UseQueryOptions<TResponse, Error, TResponse, readonly unknown[]>;
}
export interface EndpointWithoutInput<TResponse> {
call(): Promise<TResponse>;
queryKey(): readonly unknown[];
queryOptions(
config?: QueryConfig<TResponse>,
): UseQueryOptions<TResponse, Error, TResponse, readonly unknown[]>;
}
export type Endpoint<TInput, TResponse> = TInput extends void
? EndpointWithoutInput<TResponse>
: EndpointWithInput<TInput, TResponse>;
// ---------------------------------------------------------------------------
// Endpoint builder
// ---------------------------------------------------------------------------
export function endpoint<TInput, TResponse>(
service: string,
action: string,
execute: (input: TInput) => Promise<TResponse>,
queryKeyBuilder?: (input: TInput) => readonly unknown[],
) {
const buildKey = (input?: TInput): readonly unknown[] => {
if (queryKeyBuilder && input !== undefined) {
return queryKeyBuilder(input as TInput);
}
return input === undefined
? [service, action]
: [service, action, input];
};
const call = (input: TInput) => execute(input);
const queryKey = (input?: TInput) => buildKey(input);
const queryOptions = (
config?: { input?: TInput } & QueryConfig<TResponse>,
): UseQueryOptions<
TResponse,
Error,
TResponse,
readonly unknown[]
> => {
const { input, ...rest } = config ?? {};
return {
...rest,
queryKey: buildKey(input),
queryFn: () => execute(input as TInput),
};
};
const mutationOptions = (
config?: Omit<
UseMutationOptions<
TResponse,
Error,
TInput
>,
"mutationFn"
>,
): UseMutationOptions<
TResponse,
Error,
TInput
> => {
return {
...config,
mutationFn: (
variables: TInput,
): Promise<TResponse> =>
execute(variables),
};
};
return {
call,
queryKey,
queryOptions,
mutationOptions
};
}
// ---------------------------------------------------------------------------
// Helper utilities
// ---------------------------------------------------------------------------
export function unwrap<T>(response: { data: T } | T): T {
if (
response &&
typeof response === "object" &&
"data" in (response as object)
) {
return (response as { data: T }).data;
}
return response as T;
}