Added openapi based api client
This commit is contained in:
185
apps/frontend/app/generated/tanstack-client.ts
Normal file
185
apps/frontend/app/generated/tanstack-client.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import type {
|
||||
EndpointByMethod,
|
||||
ApiClient,
|
||||
SuccessStatusCode,
|
||||
ErrorStatusCode,
|
||||
InferResponseByStatus,
|
||||
TypedSuccessResponse,
|
||||
} from "./api-client.ts";
|
||||
import { errorStatusCodes, TypedStatusError } from "./api-client.ts";
|
||||
|
||||
type EndpointQueryKey<TOptions extends EndpointParameters> = [
|
||||
TOptions & {
|
||||
_id: string;
|
||||
_infinite?: boolean;
|
||||
},
|
||||
];
|
||||
|
||||
const createQueryKey = <TOptions extends EndpointParameters>(
|
||||
id: string,
|
||||
options?: TOptions,
|
||||
infinite?: boolean,
|
||||
): [EndpointQueryKey<TOptions>[0]] => {
|
||||
const params: EndpointQueryKey<TOptions>[0] = { _id: id } as EndpointQueryKey<TOptions>[0];
|
||||
if (infinite) {
|
||||
params._infinite = infinite;
|
||||
}
|
||||
if (options?.body) {
|
||||
params.body = options.body;
|
||||
}
|
||||
if (options?.header) {
|
||||
params.header = options.header;
|
||||
}
|
||||
if (options?.path) {
|
||||
params.path = options.path;
|
||||
}
|
||||
if (options?.query) {
|
||||
params.query = options.query;
|
||||
}
|
||||
return [params];
|
||||
};
|
||||
|
||||
// <EndpointByMethod.Shorthands>
|
||||
export type GetEndpoints = EndpointByMethod["get"];
|
||||
// </EndpointByMethod.Shorthands>
|
||||
|
||||
// <ApiClientTypes>
|
||||
export type EndpointParameters = {
|
||||
body?: unknown;
|
||||
query?: Record<string, unknown>;
|
||||
header?: Record<string, unknown>;
|
||||
path?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type RequiredKeys<T> = {
|
||||
[P in keyof T]-?: undefined extends T[P] ? never : P;
|
||||
}[keyof T];
|
||||
|
||||
type MaybeOptionalArg<T> = RequiredKeys<T> extends never ? [config?: T] : [config: T];
|
||||
|
||||
type InferResponseData<TEndpoint, TStatusCode> =
|
||||
TypedSuccessResponse<any, any, any> extends InferResponseByStatus<TEndpoint, TStatusCode>
|
||||
? Extract<InferResponseByStatus<TEndpoint, TStatusCode>, { data: {} }>["data"]
|
||||
: Extract<InferResponseByStatus<TEndpoint, TStatusCode>["data"], {}>;
|
||||
|
||||
// </ApiClientTypes>
|
||||
|
||||
// <ApiClient>
|
||||
export class TanstackQueryApiClient {
|
||||
constructor(public client: ApiClient) {}
|
||||
|
||||
// <ApiClient.get>
|
||||
get<Path extends keyof GetEndpoints, TEndpoint extends GetEndpoints[Path]>(
|
||||
path: Path,
|
||||
...params: MaybeOptionalArg<TEndpoint["parameters"]>
|
||||
) {
|
||||
const queryKey = createQueryKey(path as string, params[0]);
|
||||
const query = {
|
||||
/** type-only property if you need easy access to the endpoint params */
|
||||
"~endpoint": {} as TEndpoint,
|
||||
queryKey,
|
||||
queryFn: {} as "You need to pass .queryOptions to the useQuery hook",
|
||||
queryOptions: queryOptions({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const requestParams = {
|
||||
...(params[0] || {}),
|
||||
...(queryKey[0] || {}),
|
||||
overrides: { signal },
|
||||
withResponse: false as const,
|
||||
};
|
||||
const res = await this.client.get(path, requestParams as never);
|
||||
return res as InferResponseData<TEndpoint, SuccessStatusCode>;
|
||||
},
|
||||
queryKey: queryKey,
|
||||
}),
|
||||
};
|
||||
|
||||
return query;
|
||||
}
|
||||
// </ApiClient.get>
|
||||
|
||||
// <ApiClient.request>
|
||||
/**
|
||||
* Generic mutation method with full type-safety for any endpoint; it doesnt require parameters to be passed initially
|
||||
* but instead will require them to be passed when calling the mutation.mutate() method
|
||||
*/
|
||||
mutation<
|
||||
TMethod extends keyof EndpointByMethod,
|
||||
TPath extends keyof EndpointByMethod[TMethod],
|
||||
TEndpoint extends EndpointByMethod[TMethod][TPath],
|
||||
TWithResponse extends boolean = false,
|
||||
TSelection = TWithResponse extends true
|
||||
? InferResponseByStatus<TEndpoint, SuccessStatusCode>
|
||||
: InferResponseData<TEndpoint, SuccessStatusCode>,
|
||||
TError = TEndpoint extends { responses: infer TResponses }
|
||||
? TResponses extends Record<string | number, unknown>
|
||||
? TypedStatusError<InferResponseData<TEndpoint, ErrorStatusCode>>
|
||||
: Error
|
||||
: Error,
|
||||
>(
|
||||
method: TMethod,
|
||||
path: TPath,
|
||||
options?: {
|
||||
withResponse?: TWithResponse;
|
||||
selectFn?: (
|
||||
res: TWithResponse extends true
|
||||
? InferResponseByStatus<TEndpoint, SuccessStatusCode>
|
||||
: InferResponseData<TEndpoint, SuccessStatusCode>,
|
||||
) => TSelection;
|
||||
throwOnStatusError?: boolean;
|
||||
throwOnError?: boolean | ((error: TError) => boolean);
|
||||
},
|
||||
) {
|
||||
const mutationKey = [{ method, path }] as const;
|
||||
const mutationFn = async (
|
||||
params: (TEndpoint extends { parameters: infer Parameters } ? Parameters : {}) & {
|
||||
throwOnStatusError?: boolean;
|
||||
overrides?: RequestInit;
|
||||
},
|
||||
): Promise<TSelection> => {
|
||||
const withResponse = options?.withResponse ?? false;
|
||||
const throwOnStatusError =
|
||||
params.throwOnStatusError ?? options?.throwOnStatusError ?? (withResponse ? false : true);
|
||||
const selectFn = options?.selectFn;
|
||||
const response = await (this.client as any)[method](path, {
|
||||
...(params as any),
|
||||
withResponse: true,
|
||||
throwOnStatusError: false,
|
||||
});
|
||||
|
||||
if (throwOnStatusError && errorStatusCodes.includes(response.status as never)) {
|
||||
throw new TypedStatusError(response as never);
|
||||
}
|
||||
|
||||
// Return just the data if withResponse is false, otherwise return the full response
|
||||
const finalResponse = withResponse ? response : response.data;
|
||||
const res = selectFn ? selectFn(finalResponse as any) : finalResponse;
|
||||
return res as never;
|
||||
};
|
||||
return {
|
||||
/** type-only property if you need easy access to the endpoint params */
|
||||
"~endpoint": {} as TEndpoint,
|
||||
mutationKey: mutationKey,
|
||||
mutationFn: {} as "You need to pass .mutationOptions to the useMutation hook",
|
||||
mutationOptions: {
|
||||
throwOnError: options?.throwOnError as boolean | ((error: TError) => boolean),
|
||||
mutationKey: mutationKey,
|
||||
mutationFn: mutationFn,
|
||||
} as Omit<
|
||||
import("@tanstack/react-query").UseMutationOptions<
|
||||
TSelection,
|
||||
TError,
|
||||
(TEndpoint extends { parameters: infer Parameters } ? Parameters : {}) & {
|
||||
withResponse?: boolean;
|
||||
throwOnStatusError?: boolean;
|
||||
}
|
||||
>,
|
||||
"mutationFn"
|
||||
> & {
|
||||
mutationFn: typeof mutationFn;
|
||||
},
|
||||
};
|
||||
}
|
||||
// </ApiClient.request>
|
||||
}
|
||||
Reference in New Issue
Block a user