Skip to main content
Version: 5.x.x

Custom Adapter

You can create your own adapter based on your preferences or requirements using bindings. They let you easily connect into the library logic flow, which enables you to make your custom adapter without handling the library internals.

We propose to use the getAdapterBindings utility, so you can skip the sensitive and highly advanced part of connecting into the Hyper Fetch flow.

Use makeRequest as the wrapper for resolving your request. It will handle all pre and post validators with all the async operations that happen inside Hyper Fetch. This is a simple way of making custom adapters.


Example

const customHttpAdapter = async () => {
const {
makeRequest,
fullUrl,
method,
headers,
payload,
config,
getAbortController,
getRequestStartTimestamp,
getResponseStartTimestamp,
createAbortListener,
onBeforeRequest,
onRequestStart,
onRequestProgress,
onRequestEnd,
onResponseStart,
onResponseProgress,
onResponseEnd,
onSuccess,
onAbortError,
onTimeoutError,
onUnexpectedError,
onError,
} = await getAdapterBindings(request, requestId);

// Wrap your function with make request to provide all lifecycle and async methods
makeRequest((resolve) => {
// ... any custom adapter logic, you can see axios implementation bellow
resolve(
axios({
method,
url: fullUrl,
data: payload,
headers,
timeout: config.timeout,
}),
);
});
};

Setup

You can connect any adapter to communicate with the server and handle it as you want. The only thing that has to match is the interface of the output.

import { BaseAdapterType } from "@hyper-fetch/core";

const customHttpAdapter: BaseAdapterType = (request: RequestInstance) =>
Promise.resolve({ data: null, error: null, status: 0 });

const client = new Client({ url }).setAdapter(customHttpAdapter);

Typescript

You can pass custom options to your adapter from global setup and while request initialization. BaseAdapterType lets you state the correct specification for your adapter. You can decide what custom options it consumes, how statuses look in your adapter, what methods are allowed, what query params are default, and what additional data it returns from requests.

import { BaseAdapterType, HttpMethodsType, HttpStatusType, QueryParamsType } from "@hyper-fetch/core";

type MyCustomAdapterOptions = {
timeout?: number;
someOtherOption?: boolean;
};

type MyCustomExtra = {
headers: string;
raw: string;
json: string;
};

const customHttpAdapter: BaseAdapterType<
MyCustomAdapterOptions,
HttpMethodsType,
HttpStatusType,
MyCustomExtra,
QueryParamsType
> = (request: RequestInstance) => Promise.resolve({ data: null, error: null, status: 0 });

const client = new Client<Error>({ url }).setAdapter(customHttpAdapter);

Adapter Unions

You can provide adapter unions. This means you can specify exact data for the given option sets in your adapters. This allows you to create custom rules for using your adapter. For example, you can create a rule for the GET method that states it returns raw data in extra (i.e. so users will not be able to use it with other methods). We used this pattern a lot in the Firebase adapter.

type MyAdapter =
| BaseAdapterType<{ timeout?: number }, HttpMethodsType.GET, HttpStatusType, { raw: string }, QueryParamsType>
| BaseAdapterType<{ headers?: string }, HttpMethodsType.POST, HttpStatusType, { json: string }, QueryParamsType>;

const customHttpAdapter: MyAdapter = (request: RequestInstance) =>
Promise.resolve({ data: null, error: null, status: 0 });

const client = new Client<Error>({ url }).setAdapter(customHttpAdapter);

// We decide which type to used based on passed config

const request1 = client.createRequest()({
endpoint: "/product",
method: HttpMethodsType.GET,
options: { timeout: 1000 },
});

const {
extra: { raw },
} = await request1.send();

// We decide which type to use based on passed config

const request2 = client.createRequest()({
endpoint: "/user",
method: HttpMethodsType.POST,
options: { headers: "" },
});

const {
extra: { json },
} = await request2.send();