@risemaxi/api-client
Version:
Client Library for Rise
196 lines (195 loc) • 6.73 kB
JavaScript
import { useInfiniteQuery, useMutation, useQuery, useQueryClient, } from "@tanstack/react-query";
export class RiseApiClient {
fetcher;
#baseUrl = "";
#enabledParsing = true;
#valueModule;
#contractModule;
constructor(fetcher) {
this.fetcher = fetcher;
}
setBaseUrl(baseUrl) {
this.#baseUrl = baseUrl;
return this;
}
setEnableParsing(enable) {
this.#enabledParsing = enable;
return this;
}
async #getValueModule() {
if (!this.#valueModule) {
this.#valueModule = import("@sinclair/typebox/value");
}
return this.#valueModule;
}
async #getContractModule() {
if (!this.#contractModule) {
this.#contractModule = import("./contract.js");
}
return this.#contractModule;
}
async #parse(schema, value) {
if (!this.#enabledParsing) {
return value;
}
const { Value } = await this.#getValueModule();
return Value.Parse(schema, value);
}
#constructPath(template, params) {
if (!params) {
return template;
}
return template.replace(/{(\w+)}/g, (match, key) => {
if (key in params) {
return params[key];
}
return match;
});
}
async #request(method, path, ...params) {
const parameters = params[0];
const finalPath = this.#constructPath(path, parameters?.path);
if (!this.#enabledParsing) {
return this.fetcher(method, this.#baseUrl + finalPath, parameters);
}
const { getEndpointSchema } = await this.#getContractModule();
const endpointSchema = getEndpointSchema(method, path);
if (!endpointSchema?.properties) {
throw new Error(`Unknown endpoint schema for ${method.toUpperCase()} ${String(path)}`);
}
const endpointProperties = endpointSchema.properties;
const parsedParameters = endpointProperties.parameters
? await this.#parse(endpointProperties.parameters, parameters)
: parameters;
return this.#parse(endpointProperties.response, await this.fetcher(method, this.#baseUrl + finalPath, parsedParameters));
}
get(path, ...params) {
return this.#request("get", path, ...params);
}
post(path, ...params) {
return this.#request("post", path, ...params);
}
patch(path, ...params) {
return this.#request("patch", path, ...params);
}
delete(path, ...params) {
return this.#request("delete", path, ...params);
}
put(path, ...params) {
return this.#request("put", path, ...params);
}
}
export function createRiseApiClient(fetcher) {
return new RiseApiClient(fetcher);
}
export class RiseApiHooks {
#client;
constructor(client) {
this.#client = client;
}
getCacheKey(method, path, ...params) {
const key = `${method}_${path}`;
return [key, ...params];
}
setCachedData(queryClient, method, path, ...options) {
const [data, ...params] = options;
const queryKey = this.getCacheKey(method, path, ...params);
queryClient.setQueryData(queryKey, data);
}
useSetCachedData(method, path, ...options) {
const queryClient = useQueryClient();
this.setCachedData(queryClient, method, path, ...options);
}
getCachedData(queryClient, method, path, ...params) {
const queryKey = this.getCacheKey(method, path, ...params);
return queryClient.getQueryData(queryKey);
}
useGetCachedData(method, path, ...params) {
const queryClient = useQueryClient();
return this.getCachedData(queryClient, method, path, ...params);
}
prefetchData(queryClient, path, ...rest) {
const [config, options] = rest;
const queryKey = this.getCacheKey("get", path, config);
const queryFn = () => this.#client.get(path, config);
queryClient.prefetchQuery({
...options,
queryKey,
queryFn,
});
}
usePrefetchData(path, ...rest) {
const queryClient = useQueryClient();
this.prefetchData(queryClient, path, ...rest);
}
useGet(path, ...rest) {
const queryClient = useQueryClient();
const [config, options] = rest;
const queryKey = this.getCacheKey("get", path, config);
const invalidate = () => queryClient.invalidateQueries({
exact: true,
queryKey,
});
return Object.assign(useQuery({
queryFn: () => this.#client.get(path, config),
queryKey,
...options,
}), { invalidate, queryKey });
}
useInfiniteGet(path, configMapper, options) {
const queryClient = useQueryClient();
const queryKey = this.getCacheKey("get", path, configMapper({
meta: {},
queryKey: [],
signal: new AbortController().signal,
client: queryClient,
}));
const queryFn = (context) => {
const config = configMapper(context);
return this.#client.get(path, config);
};
const invalidate = () => queryClient.invalidateQueries({
queryKey,
});
return Object.assign(useInfiniteQuery({
queryFn,
queryKey,
...options,
}), { invalidate, queryKey });
}
usePost(path, options) {
const mutationKey = this.getCacheKey("post", path, {});
return Object.assign(useMutation({
mutationFn: (params) => this.#client.post(path, params),
mutationKey,
...options,
}), { mutationKey });
}
usePatch(path, options) {
const mutationKey = this.getCacheKey("patch", path, {});
return Object.assign(useMutation({
mutationFn: (params) => this.#client.patch(path, params),
mutationKey,
...options,
}), { mutationKey });
}
useDelete(path, options) {
const mutationKey = this.getCacheKey("delete", path, {});
return Object.assign(useMutation({
mutationFn: (params) => this.#client.delete(path, params),
mutationKey,
...options,
}), { mutationKey });
}
usePut(path, options) {
const mutationKey = this.getCacheKey("put", path, {});
return Object.assign(useMutation({
mutationFn: (params) => this.#client.put(path, params),
mutationKey,
...options,
}), { mutationKey });
}
}
export function createRiseApiHooks(client) {
return new RiseApiHooks(client);
}