@ackee/antonio-core
Version:
A HTTP client built on fetch API with axios-like API.
139 lines (138 loc) • 4.57 kB
TypeScript
import type { DefaultRequestConfig } from './modules/request/config';
import type { IterableStream } from './modules/response/iterableStream';
export type Primitive = bigint | boolean | null | number | string | undefined;
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
export type RequestBodyData = BodyInit | Primitive | object | any[];
export type RequestBody = BodyInit;
/**
* It defines the format of the returned `data` property in the request result object (`RequestResult`):
* - `json` -> `data === await response.json()`
* - `text` -> `data === await response.text()`
* - ...
*
* #### Default behavior based on `Content-Type` header
*
* Antonio selects the `responseDataType` by default based on the response `Content-Type` header (its mime type).
* Based on that it chooses method for formatting the body:
* - `Content-Type: application/json` refers to `response.json()` method.
* - `text/*` -> `response.text()` method.
* - `image/*`, `audio/*`, `video/*`, `application/octet-stream` -> `response.arrayBuffer()`.
* - `multipart/form-data` -> `response.formData()`
*
* Without `Content-Type` or without explicitly defined `responseDataType`, it returns `data` as `null`.
*
* #### Exception with `Content-Length` header
*
* Note that if the `Content-Length` header is `'0'`, then formatting body is skip completely and `data` are `null`.
*
* #### Streaming the response
* Selecting `iterableStream` response data type fetches data with `ReadableStream`.
* The `data` is going to be async generator that yields out slices of data.
* If the `Content-Type` response header has `application/json` mime type,
* then the received chunk of string from the stream is going to be parsed as json
* and the `slice` and once a valid json is created, it's yielded out as the `slice`.
*
* __Note that both examples are functionally identical.__
*
* _#1 Using async generators:_
* @example
* ```ts
* async function* fetchPosts() {
* const { data } = yield* api.get('/posts', {
* responseDataType: 'iterableStream',
* });
*
* for await (const slice of data) {
* console.log(slice) // -> [{ title: '1' }, { title: '2' }, ...]
* }
* }
* ```
*
* _#2 Using sync generators:_
* @example
* ```ts
* import { runIterableStream } from `@ackee/antonio-core`;
*
* function* fetchPosts() {
* const { data } = yield* api.get('/posts', {
* responseDataType: 'iterableStream',
* });
*
* yield runIterableStream(data, function*(slice) {
* console.log(slice) // -> [{ title: '1' }, { title: '2' }, ...]
* });
* }
* ```
*/
export type ResponseDataType = 'json' | 'blob' | 'formData' | 'text' | 'arrayBuffer' | 'iterableStream' | 'stream';
export type ResponseData = ArrayBuffer | FormData | ReadableStream<Uint8Array> | ArrayBufferView | Blob | Primitive | object | any[] | IterableStream | null;
export type RequestHeaders = Headers | HeadersInit | {
[key: string]: string;
};
/**
* @example
* ```ts
* const { data } = yield* api.get('/user/:id', {
* uriParams: { id: '1' }
* });
*
* console.assert(data.id === '1');
* ```
*/
export type RequestUriParams = {
[key: string]: string | number;
};
/**
* An instace of `URLSearchParams` or a plain object.
* @example
* ```ts
* api.get('/posts', {
* params: new URLSearchParams({
* page: 1,
* limit: 20,
* })
* });
* ```
* @example
* ```ts
* api.get('/posts', {
* params: {
* page: 1,
* limit: 20,
* userIds: [1, 2, 3]
* }
* });
* ```
*/
export type RequestSearchParams = URLSearchParams | {
[key: string]: string | number | boolean | (string | number | boolean)[];
};
export interface RequestConfig extends Omit<RequestInit, 'body' | 'headers' | 'method'> {
/**
* `baseURL` will be prepended to `url` unless `url` is absolute.
* It can be convenient to set `baseURL` for an instance of antonio to pass relative URLs.
*/
baseURL?: string;
responseDataType?: ResponseDataType;
uriParams?: RequestUriParams;
headers?: RequestHeaders;
params?: RequestSearchParams;
}
export interface RequestResult<D> {
request: Request;
response: Response;
data: D;
}
export interface FinalRequestConfig extends DefaultRequestConfig {
params?: URLSearchParams;
}
export interface RequestParams {
url: string;
config?: RequestConfig;
bodyData: RequestBodyData;
}
export interface FinalRequestParams {
url: string;
config: FinalRequestConfig;
bodyData: RequestBodyData;
}