UNPKG

@danstackme/apity

Version:

Type-safe API client generator for React applications with file-based routing and runtime validation

122 lines (115 loc) 6.47 kB
import * as _tanstack_react_query from '@tanstack/react-query'; import { UseQueryOptions, UseMutationOptions, QueryClient } from '@tanstack/react-query'; import { AxiosInstance } from 'axios'; import * as react_jsx_runtime from 'react/jsx-runtime'; import { z } from 'zod'; type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; interface Register { } type ConsumerFetchEndpoints = Register extends { fetchEndpoints: infer T; } ? T : never; type ConsumerMutateEndpoints = Register extends { mutateEndpoints: infer T; } ? T : never; interface ApiEndpoint<TMethod extends HttpMethod, TResponse = unknown, TBody = unknown, TQuery = unknown> { method: TMethod; response?: TResponse; body?: TBody; query?: TQuery; } type AnyApiEndpoint = ApiEndpoint<any, any, any, any>; type FetchEndpoint<TResponse, TBody, TQuery> = ApiEndpoint<"GET", TResponse, TBody, TQuery>; type MutateEndpoint<TMethod extends Exclude<HttpMethod, "GET">, TResponse, TBody, TQuery> = ApiEndpoint<TMethod, TResponse, TBody, TQuery>; type ApiEndpoints = { [path: string]: FetchEndpoint<any, any, any>[] | MutateEndpoint<Exclude<HttpMethod, "GET">, any, any, any>[] | readonly ApiEndpoint<HttpMethod>[]; }; interface ApiConfig { baseUrl: string; queryClient?: QueryClient; client?: AxiosInstance; headers?: Record<string, string>; fetchEndpoints: ApiEndpoints; mutateEndpoints: ApiEndpoints; middleware?: { before?: (config: any) => any; after?: (response: any) => any; onError?: (error: any) => any; }; } interface ApiContext { client: AxiosInstance; queryClient: QueryClient; config: ApiConfig; middleware: ((config: any) => any)[]; fetchEndpoints: ApiEndpoints; mutateEndpoints: ApiEndpoints; } type GetPathParamType<TPath extends string> = HasPathParams<TPath> extends true ? { params: ExtractPathParams<TPath>; } : { params?: ExtractPathParams<TPath>; }; type UseFetchOptionsType<TPath extends FetchPath> = Omit<UseQueryOptions<FetchResponseFor<TPath, "GET">, Error, FetchResponseFor<TPath, "GET">, any>, "queryKey" | "queryFn"> & { path: TPath; } & GetPathParamType<TPath> & GetQueryParamType<TPath, "GET">; type UseMutateOptionsType<TPath extends MutatePath, TMethod extends AvailableMutateMethods<TPath>> = Omit<UseMutationOptions<MutateResponseFor<TPath, TMethod>, Error, MutateBodyFor<TPath, TMethod>>, "mutationFn"> & { path: TPath; method: TMethod; } & GetPathParamType<TPath> & GetQueryParamType<TPath, TMethod>; type GetQueryParamType<TPath extends FetchPath | MutatePath, TMethod extends HttpMethod, TEndpoint extends AnyApiEndpoint = EndpointsByMethod<TPath, TMethod>> = HasRequiredField<NonNullable<TEndpoint["query"]>> extends true ? NonNullable<RequiredQueryParams<TEndpoint>> : NonNullable<OptionalQueryParams<TEndpoint>>; type ExtractPathParams<TPath extends string> = TPath extends `${string}[${infer Param}]${infer Rest}` ? { [K in Param]: string | number; } & (Rest extends `${string}[${string}]${string}` ? ExtractPathParams<Rest> : {}) : {}; type FetchPath = keyof ConsumerFetchEndpoints & string; type MutatePath = keyof ConsumerMutateEndpoints & string; type MutateMethodsFor<P extends MutatePath, M extends HttpMethod> = Extract<ConsumerMutateEndpoints[P][number]["method"], M> extends never ? never : M; type MutateResponseFor<P extends MutatePath, M extends HttpMethod> = Extract<ConsumerMutateEndpoints[P][number]["method"], M> extends never ? never : Extract<ConsumerMutateEndpoints[P][number], { method: M; }>["response"]; type MutateBodyFor<P extends MutatePath, M extends HttpMethod> = Extract<ConsumerMutateEndpoints[P][number]["method"], M> extends never ? never : Extract<ConsumerMutateEndpoints[P][number], { method: M; }>["body"]; type FetchResponseFor<P extends FetchPath, M extends HttpMethod> = Extract<ConsumerFetchEndpoints[P][number]["method"], M> extends never ? never : Extract<ConsumerFetchEndpoints[P][number], { method: M; }>["response"]; type AvailableMutateMethods<P extends MutatePath> = MutateMethodsFor<P, "POST"> | MutateMethodsFor<P, "PUT"> | MutateMethodsFor<P, "DELETE"> | MutateMethodsFor<P, "PATCH">; type EndpointsByMethod<TPath extends FetchPath | MutatePath, TMethod extends HttpMethod> = TMethod extends "GET" ? Extract<ConsumerFetchEndpoints[TPath][number], { method: "GET"; }> : Extract<ConsumerMutateEndpoints[TPath][number], { method: TMethod; }>; type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K]; } ? never : K; }[keyof T]; type HasRequiredField<T> = RequiredKeys<T> extends never ? false : true; type HasPathParams<T extends string> = T extends `${string}[${string}]${string}` ? true : false; type OptionalQueryParams<TEndpoint extends AnyApiEndpoint> = { query?: NonNullable<TEndpoint["query"]>; }; type RequiredQueryParams<TEndpoint extends AnyApiEndpoint> = { query: NonNullable<TEndpoint["query"]>; }; declare function useFetch<TPath extends FetchPath>(options: UseFetchOptionsType<TPath>): _tanstack_react_query.UseQueryResult<FetchResponseFor<TPath, "GET">, Error>; declare function useMutate<TPath extends MutatePath, TMethod extends AvailableMutateMethods<TPath>>(options: UseMutateOptionsType<TPath, TMethod>): _tanstack_react_query.UseMutationResult<MutateResponseFor<TPath, TMethod>, Error, MutateBodyFor<TPath, TMethod>, unknown>; interface ApiContextValue { client: AxiosInstance; queryClient: ApiContext["queryClient"]; config: ApiContext["config"]; } declare function useApiContext(): ApiContextValue; interface ApiProviderProps { children: React.ReactNode; api: ApiContext; } declare function ApiProvider({ children, api }: ApiProviderProps): react_jsx_runtime.JSX.Element; declare function createApiEndpoint<TMethod extends HttpMethod, TResponse = unknown, TBody = unknown, TQuery = unknown>(config: { method: TMethod; response?: z.ZodType<TResponse>; body?: z.ZodType<TBody>; query?: z.ZodType<TQuery>; }): TMethod extends "GET" ? FetchEndpoint<TResponse, TBody, TQuery> : TMethod extends Exclude<HttpMethod, "GET"> ? MutateEndpoint<TMethod, TResponse, TBody, TQuery> : never; declare function createApi(config: ApiConfig): ApiContext; export { type ApiConfig, type ApiContext, type ApiEndpoint, type ApiEndpoints, ApiProvider, type FetchEndpoint, type HttpMethod, type MutateEndpoint, type Register, createApi, createApiEndpoint, useApiContext, useFetch, useMutate };