UNPKG

@volverjs/data

Version:

Repository pattern implementation with a tiny HttpClient based on Fetch API.

268 lines (267 loc) 9.6 kB
import type { HttpClientInstance, HttpClientRequestOptions, HttpClientUrlTemplate } from './HttpClient'; import type { Repository } from './Repository'; import type { ParamMap } from './types'; type RepositoryHttpReadPendingRequest<Type> = { responsePromise: Promise<{ ok: boolean; aborted?: boolean; abortReason?: string; data?: Type[]; item?: Type; metadata?: ParamMap; }>; abort: (reason?: string) => void; signal: AbortSignal; count: number; }; export type RepositoryHttpReadOptions = HttpClientRequestOptions & { key?: string | number | boolean; }; export type RepositoryHttpOptions<Type, TResponse = unknown> = { /** * The httpClient instance scope (name) * @default undefined * @example * ```typescript * addHttpClient('v2', { prefixUrl: 'https://myapi.com/v2' }) * const { read } = useRepositoryHttp<{ id: string }>('users/?:id', { httpClientScope: 'v2' }) * read({ id: 1 }) * //=> GET https://myapi.com/v2/?id=1 * ``` */ httpClientScope?: string; /** * The prefix url to use for all requests. * @default undefined * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/?:id', { httpClientOptions: { prefixUrl: 'https://example.com' } }) * repository.read({ id: 1 }) * //=> GET https://example.com/?id=1 * ``` */ httpClientOptions?: HttpClientRequestOptions; /** * A function to transform the raw response data into the expected data type. * @remarks * Must return an array of items. * @default * `(raw: unknown) => Array.isArray(raw) ? raw : ([raw] as Type[])` * @example * ```typescript * const responseAdapter = (raw) => [new Type(raw)] * const repository = new RepositoryHttp(client, 'users/?:id', { responseAdapter }) * ``` */ responseAdapter?: (raw: TResponse) => Type[]; /** * A function to transform the request data into the expected data type. * @default * `(item: Type): unknown => item` * @example * ```typescript * const requestAdapter = (item) => ({ ...item, foo: 'bar' }) * const repository = new RepositoryHttp(client, 'users/?:id', { requestAdapter }) * ``` */ requestAdapter?: (item: Type) => unknown; /** * A function to extract metadata from the response. * @default * `(response: Response): ParamMap | undefined => { * let toReturn = undefined * if (response.headers.has('Content-Language')) { * toReturn = { * contentLanguage: response.headers.get('Content-Language'), * } * } * if (response.headers.has('Accept-Language')) { * toReturn = { * acceptLanguage: response.headers.get('Accept-Language'), * } * } * if (response.headers.has('X-Total-Count')) { * toReturn = { * ...toReturn, * total: response.headers.get('X-Total-Count'), * } * } * return toReturn * }` * @example * ```typescript * const metadataAdapter = (response) => { * if (response.headers.has('X-Pagination')) { * return JSON.parse(response.headers.get('X-Pagination')) * } * return undefined * } * const repository = new RepositoryHttp(client, 'users/?:id', { metadataAdapter }) * ``` */ metadataAdapter?: (response: Response) => ParamMap; /** * A function to generate a key for the request. * @default * `(str: string) => Hash.cyrb53(str)` * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/?:id', { hashFunction: Hash.djb2 }) * ``` */ hashFunction?: (str: string) => number; /** * The class to apply to the items. An alternative to `responseAdapter`. * @default undefined * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/?:id', { class: Type }) * ``` */ class?: new (...args: any[]) => Type; }; export declare class RepositoryHttp<Type, TResponse = unknown> implements Repository<Type> { private _client; private _template; private _responseAdapter; private _requestAdapter; private _metadataAdapter; private _hashFunction; private _readPendingRequests; private _httpClientOptions?; /** * @param client The HTTP client to use. * @param template The URL template to use for requests. * @param options The options to use. */ constructor(client: HttpClientInstance, template: string | HttpClientUrlTemplate, options?: RepositoryHttpOptions<Type, TResponse>); /** * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type') * const { response, abort } = repository.read({ type: 'admin', page: 1 }) * const { data, item, metadata, ok } = await response * //=> GET /users/admin?page=1 * ``` */ read: (params?: ParamMap, options?: RepositoryHttpReadOptions) => Omit<RepositoryHttpReadPendingRequest<Type>, "count"> | { abort: (reason?: string) => void; responsePromise: Promise<{ data: Type[]; item: Type; metadata: ParamMap | undefined; ok: boolean; aborted?: undefined; abortReason?: undefined; } | { ok: boolean; aborted: boolean; abortReason: any; data?: undefined; item?: undefined; metadata?: undefined; }>; signal: AbortSignal; }; /** * @params payload - The payload to use in the request body. * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type') * const payload = { name: 'John' } * const { response, abort } = repository.create(payload, { type: 'admin' }) * const { data, item, metadata, ok } = await response * //=> POST /users/admin * ``` */ create: (payload?: Type | Type[], params?: ParamMap, options?: HttpClientRequestOptions) => { abort: (reason?: string) => void; responsePromise: Promise<{ data: Type[]; item: Type; metadata: ParamMap | undefined; ok: boolean; aborted?: undefined; abortReason?: undefined; } | { ok: boolean; aborted: boolean; abortReason: any; data?: undefined; item?: undefined; metadata?: undefined; }>; signal: AbortSignal; }; /** * @params payload - The payload to use in the request body. * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type/?:id') * const payload = { id: 1, name: 'John' } * const { response, abort } = repository.update(payload, { type: 'admin', id: 1 }) * const { data, item, metadata, ok } = await response * //=> PUT /users/admin/1 * ``` */ update: (payload?: Type | Type[], params?: ParamMap, options?: HttpClientRequestOptions) => { abort: (reason?: string) => void; responsePromise: Promise<{ data: Type[]; item: Type; metadata: ParamMap | undefined; ok: boolean; aborted?: undefined; abortReason?: undefined; } | { ok: boolean; aborted: boolean; abortReason: any; data?: undefined; item?: undefined; metadata?: undefined; }>; signal: AbortSignal; }; /** * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type/?:id') * const { response, abort } = repository.delete({ type: 'admin', id: 1 }) * const { ok } = await response * //=> DELETE /users/admin/1 * ``` */ remove: (params?: ParamMap, options?: HttpClientRequestOptions) => { abort: (reason?: string) => void; responsePromise: Promise<{ ok: boolean; aborted?: undefined; abortReason?: undefined; } | { ok: boolean; aborted: boolean; abortReason: any; }>; signal: AbortSignal; }; private _hasRepositoryHttpReadPendingRequest; private _deleteRepositoryHttpReadPendingRequest; private _cloneRepositoryHttpReadPendingRequest; private _setRepositoryHttpReadPendingRequest; private _requestUrl; private _requestOptions; } export {};