fry

Tiny declarative HTTP client based on the browser Fetch API

Usage no npm install needed!

<script type="module">
  import fry from 'https://cdn.skypack.dev/fry';
</script>

README

DoAsync logo

NPM Version NPM Downloads GitHub issues

Benefits over plain fetch

  • Extended API
  • Treats non-2xx status codes as errors
  • Handles JSON
  • Custom defaults
  • Easy interceptors
  • Transform function
  • And more...

fry is written in TypeScript


Installation

npm install fry

Description

fry is fetch on steroids :)

There are two exports in the package: request and createRequest

createRequest is used to create a request with predefined config

The request accepts the same params/options as fetch as well as additional ones: baseUrl, url, data, params, fn, silent

url - the only required param,
baseUrl - will be prepended to url,
data - object for json body,
params - query as an object,
silent - do not fall on http errors,
fn - preparation of the result (if necessary)

fn parameters:

request - object of the request,
response - object of the response,
jsonData - parsed json,
config - what was passed to the request

There are also basic inteceptors:

onBeforeRequest - change config,
onRequestError - return a new response (or rethrow an error),
onBeforeResponse - do something before response is handled,
onResponseError - return a new response or handle errors,

Usage

import { createRequest } from 'fry'; // import { request } from "fry";

const request = createRequest({
  baseUrl: 'https://app.io/api',
  redirect: 'error',
});

export const checkUser = id =>
  request({
    url: 'user',
    method: 'post',
    data: { userId: id }, // json
    fn: ({ jsonData: [user], config }) =>
      Boolean(config.data.userId === user.id && user.exists === true), // return boolean
  });

// No need for async / await
export const submitTransaction = tx =>
  request({
    url: 'transactions',
    method: 'post',
    data: { base64 },
    fn: ({ config, request, response, jsonData, resource, init, baseConfig }) =>
      tx, // returns tx
  });

export const activate = accountId =>
  request({
    url: `accounts/${accountId}`,
    method: 'post',
  }); // returns jsonData if no `fn`

const getAccount = accountId => request(`accounts/${accountId}`); // 'get' method is default

// Using TypeScript

const fetchCountry = async countryId =>
  request(`api/countries/${countryId}`) as Promise<Country>;

const fetchLocations = async () =>
  request<Locations>({
    url: 'api/locations/countries/',
    fn: ({ jsonData }) => convertCountriesToLocations(jsonData as Country[]),
  });

Types

Config
export type Config<R = unknown> = {
  url?: Url;
  baseUrl?: BaseUrl;
  params?: Params;
  data?: Json;
  fn?: Fn<R>;
  silent?: boolean;
  onBeforeRequest?: (config: Config<R>) => Config<R> | Promise<Config<R>>;
  onRequestError?: (meta: {
    config: Config<R>;
    request: Request;
    error: unknown;
  }) => Response | Promise<Response>;
  onBeforeResponse?: (meta: {
    config: Config<R>;
    response: Response;
    request: Request;
  }) => Response | Promise<Response>;
  onResponseError?: (meta: {
    config: Config<R>;
    response: Response;
    request: Request;
    error: Error;
  }) => Response | Promise<Response>;
} & RequestInit;
Other
export type Json = JsonPrimitive | JsonObject | JsonArray;
export type JsonPrimitive = string | number | boolean | null;
export type JsonObject = { [key: string]: Json };
export type JsonArray = Json[];

export type ObjectString = { [key: string]: string };

export type Params = string[][] | ObjectString | string | URLSearchParams;
export type Url = string;
export type BaseUrl = string;

export type Fn<R = unknown> = (meta: {
  config: ConfigFn<R>;
  request: Request;
  response: Response;
  jsonData?: unknown;
}) => R;

Repository


GitHub ★: https://github.com/doasync/fry