All Projects → YBogomolov → Fetcher Ts

YBogomolov / Fetcher Ts

Licence: mit
Type-safe wrapper around Fetch API

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Fetcher Ts

Request Compose
Composable HTTP Client
Stars: ✭ 80 (-8.05%)
Mutual labels:  http-client, fp
Restclient Cpp
C++ client for making HTTP/REST requests
Stars: ✭ 1,206 (+1286.21%)
Mutual labels:  rest-client, http-client
Ky Universal
Use Ky in both Node.js and browsers
Stars: ✭ 421 (+383.91%)
Mutual labels:  http-client, fetch
Restc Cpp
Modern C++ REST Client library
Stars: ✭ 371 (+326.44%)
Mutual labels:  rest-client, http-client
Ngx Restangular
Restangular for Angular 2 and higher versions
Stars: ✭ 787 (+804.6%)
Mutual labels:  rest-client, fetch
Vial Http
Simple http rest tool for vim
Stars: ✭ 412 (+373.56%)
Mutual labels:  rest-client, http-client
React Fetching Library
Simple and powerful API client for react 👍 Use hooks or FACCs to fetch data in easy way. No dependencies! Just react under the hood.
Stars: ✭ 561 (+544.83%)
Mutual labels:  rest-client, fetch
Requester
Powerful, modern HTTP/REST client built on top of the Requests library
Stars: ✭ 273 (+213.79%)
Mutual labels:  rest-client, http-client
Ky
🌳 Tiny & elegant JavaScript HTTP client based on the browser Fetch API
Stars: ✭ 7,047 (+8000%)
Mutual labels:  http-client, fetch
Node Fetch
A light-weight module that brings the Fetch API to Node.js
Stars: ✭ 7,176 (+8148.28%)
Mutual labels:  http-client, fetch
Fluentlyhttpclient
Http Client for .NET Standard with fluent APIs which are intuitive, easy to use and also highly extensible.
Stars: ✭ 73 (-16.09%)
Mutual labels:  rest-client, http-client
Create Request
Apply interceptors to `fetch` and create a custom request function.
Stars: ✭ 34 (-60.92%)
Mutual labels:  http-client, fetch
Redux Requests
Declarative AJAX requests and automatic network state management for single-page applications
Stars: ✭ 330 (+279.31%)
Mutual labels:  http-client, fetch
Rest Client
A tool for automated testing REST API, generating exquisite testing report and REST API documentation.
Stars: ✭ 1,181 (+1257.47%)
Mutual labels:  rest-client, http-client
Gretchen
Making fetch happen in TypeScript.
Stars: ✭ 301 (+245.98%)
Mutual labels:  http-client, fetch
Resty
Simple HTTP and REST client library for Go
Stars: ✭ 5,368 (+6070.11%)
Mutual labels:  rest-client, http-client
roast.vim
An HTTP client for Vim, that can also be used as a REST client.
Stars: ✭ 78 (-10.34%)
Mutual labels:  http-client, rest-client
Vscode Restclient
REST Client Extension for Visual Studio Code
Stars: ✭ 3,289 (+3680.46%)
Mutual labels:  rest-client, http-client
Restclient
🦄 Simple HTTP and REST client for Unity based on Promises, also supports Callbacks! 🎮
Stars: ✭ 675 (+675.86%)
Mutual labels:  rest-client, http-client
Hoodie
Hoodie is a type safe wrapper around jersey http client
Stars: ✭ 22 (-74.71%)
Mutual labels:  rest-client, http-client

Type-Safe Fetcher

npm Build Status

Motivation

Aim of this project is to provide a thin type-safe wrapper around fetch API, useful for working with JSON REST APIs.

Installation

npm install --save fetcher-ts

As this project is a part of fp-ts ecosystem, you'll also need fp-ts and io-ts as a peer dependencies. And don't forget to install a cross-fetch as a peer dependency as well – it provides an isomorphic implementation of fetch using whatwg-fetch and node-fetch:

npm install --save fp-ts io-ts cross-fetch

Usage example

Let's dive into an example right away!

// This is main business model – basically, any interface serializable to JSON you can imagine
type User = { name: string };
// And this is a model for HTTP 422 response code – it contains some internal code plus correlation ID from logging system
type FourTwoTwo = { code: number; correlationId: string };

// Type of possible server responses. It should extend `Result<Code, T>` from `fetcher`:
type GetUserResult =
  | { code: 200, payload: User[] } // 200 OK – we got the result
  | { code: 400, payload: Error } // 400 Bad Request – we did something wrong
  | { code: 401, payload: [Error, string] } // 401 Unauthorized – we tried requesting a resource we don't have access to
  | { code: 422, payload: FourTwoTwo }; // 422 Unprocessable entity – business logic error from some internal system

// `io-ts` validators for 200 and 422 responses.
// Please note that they are optional – if they are not passed to `.handle()`, the validation stage will be skipped.
const TUsers = io.array(io.type({ name: io.string }));
const TFourTwoTwo = io.type({ code: io.number, correlationId: io.string });

const [n, errors] = 
  // We create an instance of `Fetcher` class and parameterize it with our response type and final transformation result we want:
  await new Fetcher<GetUserResult, string>('https://example.com')
    // In 200 handler we need to pass a function from `User[]` to `string`, as specified in `Fetcher` parameters:
    .handle(200, (users) => users.map((u) => u.name).join(', '), TUsers)
    // In 400 handler we need to handle plain `Error`:
    .handle(400, (err) => err.message)
    // In 422 we need to deal with internal error code and correlation ID:
    .handle(
      422,
      ({ correlationId }) => correlationId,
      TFourTwoTwo,
      // For the sake of brewity I use non-null assertion here; in real code you should check for presence:
      async (res) => ({ code: +res.headers.get('x-code')!, correlationId: res.headers.get('x-correlation-id')! }),
    )
    // In 401 handler we get as a response name of permission we lack:
    .handle(401, ([err, permission]) => `You lack ${permission}. Also, ${err.message}`)
    // We CANNOT specify explicit handlers for codes we didn't describe in the `GetUserResult` type:
    // .handle(500, () => `Argument of type '500' is not assignable to parameter of type 'never'`)
    // However, we can use `discardRest` to specify a "fallback" thunk which will be executed for any codes which are not explicitly handled:
    .discardRest(() => '42')
    // `Fetcher<T, A>` is a functor in `A`, i.e. could be transformed into `Fetcher<T, B>`:
    .map((s) => s.length)
    // Finally, we can use `run` to get a `Promise<[Result, Option<io.Errors>]>`:
    .run();

// Here `n` will be a `number`, and `errors` will either be undefined, or an instance of `io.Errors`:
console.log(n, errors);

Public API

import { Fetcher } from 'fetcher-ts';

A Fetcher class is a wrapper around window.fetch with additional type safety. Its public API consists of:

Type parameters: TResult and To

TResult

Sum type of possible API endpoint responses. Should consist of a { code: number, payload: T } entries:

type MyMethodResults = 
  | { code: 200, payload: string[] } 
  | { code: 500, payload: Error };

To

A type into which the response will be transformed. Could easily be the same type as in 200 response – given that you can construct a fallback instance for all other reponse codes.

constructor(input: RequestInfo, init?: RequestInit)

Creates a new instance of a Fetcher class. Parameters are exactly the same you would normally use for window.fetch.

Please note that you'll need to pass type parameters to the constructor as well in order to ensure type inference works correctly:

type MyMethodResults = 
  | { code: 200, payload: string[] } 
  | { code: 500, payload: Error };
const fetcher = new Fetcher<MyMethodResults, string>('https://example.com');

.handle(code: number, handler: (data: From) => To, codec?: io.Type, extractor: (response: Response) => Promise): Fetcher<...>

Register a handler for given code, using optional extractor to conver the raw Response into target type From. Please note that code should be present in the passed to the constructor type parameter:

type MyMethodResults = 
  | { code: 200, payload: string[] } 
  | { code: 500, payload: Error };
const fetcher = new Fetcher<MyMethodResults, string>('https://example.com')
  .handle(400, () => 'no way'); // compilation error: Argument of type '400' is not assignable to parameter of type 'never'

Also an io-ts codec could be passed for each handler, providing validation capability for each handler:

type MyOtherMethod = { code: 400, payload: string }; // this enpoint can only fail with a text of an error :(
const [result, errors] = await new Fetcher<MyOtherMethod, string>('https://example.com/other')
  .handle(400, (msg) => `Oh noes, error: ${msg}`, io.string)
  .run();
// If the server responds not with string, an `io-ts` validation error will be present in `errors` (`Some<Errors>`).

.discardRest(restHandler: () => To): Fetcher<...>

Register a fallback handler for all HTTP status codes not registered explicitly using .handle():

type MyMethodResults = 
  | { code: 200, payload: string[] } 
  | { code: 500, payload: Error };
const fetcher = new Fetcher<MyMethodResults, string>('https://example.com')
  .handle(200, (strings) => string.join(', '))
  .discardRest(() => 'no way'); // code 500 and any other will be handled by this thunk

run(): Promise<[To, Option<io.Errors>]>

The main method to actually consume the built fetch handling chain and execute the request:

type MyMethodResults = 
  | { code: 200, payload: string[] } 
  | { code: 500, payload: Error };
const [result, validationErrors] = await new Fetcher<MyMethodResults, string>('https://example.com')
  .handle(200, (strings) => string.join(', '))
  .discardRest(() => 'no way')
  .run(); // => result: string, validationErrors: Option<io.Errors>

toTaskEither(): TaskEither<Error, [To, Option<io.Errors>]>

A convenience method to transform built fetcher chain into a TaskEither.

Use cases for this project

Such fetcher design will be beneficial for autogenerated APIs – i.e. if your result sum type is generated from something akin to OpenAPI specification. In this case the developer who uses fetcher with such sum type will always be sure that he/she handled all possible codes, as the type system will serve as a guide.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].