Very very very powerful, extensible http client for both node.js and browser.

Overview

ES-Fetch-API

中文 | English

Very very very powerful, extensible http client for both node.js and browser.

NPM npm GitHub package.json version GitHub file size in bytes GitHub issues GitHub pull requests codebeat badge

Why should you use ES-Fetch API?

Still using axios? ES-Fetch-API creates sunny world for you.

i. It's extremely light-weight and built on the native fetch API.

Comparing to axios which is ~400kB, es-fetch-api is just ~6kB. Because es-fetch-api is designed for native fetch API compatible environments.

References:

  1. fetch API on MDN
  2. fetch API on whatwg.org

ii. Enables maximized readability, extensibility, maintainability and minimized complexity.

1. The simplest example

Expected request:

GET http://yourdomain.com/api/v1/user?id=12345

Using axios:

import axios from 'axios'

// unneccessarily indicate that 'http://yourdomain.com/api/v1' means baseURL
const apiV1 = axios.create({ baseURL: 'http://yourdomain.com/api/v1' })

// unneccessarily indicate that `/user` means url
const getUser = async id => await apiV1.get({ url: `/user`, params: { id } })

const response = await getUser(12345)

Using es-fetch-api, great readability:

await apiV1(`user`, query({ id })) const response = await getUser(12345)">
import { getApi, query } from "es-fetch-api";

// without mincing words
const apiV1 = getApi('http://yourdomain.com/api/v1')

const getUser = async id => await apiV1(`user`, query({ id }))

const response = await getUser(12345)

2. More complicated example (using built-in middlewares)

Expected request:

POST http://yourdomain.com/api/v1/user/
Content-Type: application/json

{"firstName":"Fred","lastName":"Flintstone"}

Using axios:

import axios from 'axios'

const apiV1 = axios.create({ baseURL: 'http://yourdomain.com/api/v1' })

// which format is used to post data?
const createUser = async user => await apiV1.post(`/user`, user)

const resposne = await createUser({
    firstName: 'Chun',
    lastName: 'Li'
})

Using es-fetch-api, better readability:

await apiV1(`user`, POST, json(user)) const resposne = await createUser({ firstName: 'Chun', lastName: 'Li' })">
import { getApi, json, POST } from "es-fetch-api";

const apiV1 = getApi('http://yourdomain.com/api/v1')

// read what you see infomation losslessly 
const createUser = async user => await apiV1(`user`, POST, json(user))

const resposne = await createUser({
    firstName: 'Chun',
    lastName: 'Li'
})

3. Create custom middleware to extend your code while keeping better readability.

Expected request:

POST http://yourdomain.com/api/v1/user/
Content-Type: application/json
Auhorization: Token ********
X-Timestamp: ##########

{"firstName":"Fred","lastName":"Flintstone"}

Using axios:

import axios from 'axios'
import { getToken } from 'token-helper'

// easy to read? it's hard to understand they return headers.
const useToken = async () => ({ 'Authorization': `Token ${await getToken()}` })
const useTimestamp = async () => ({ 'X-Timestamp': Date.now() })

const apiV1 = axios.create({ baseURL: 'http://yourdomain.com/api/v1' })

// easy to read? Maybe or not, but too long winded to maintain.
const createUser = async user => await apiV1.post({
    url: `/user`,
    data: user,
    headers: { ...await useToken(), ...await useTimestamp() }
})

const resposne = await createUser({
    firstName: 'Chun',
    lastName: 'Li'
})

Using es-fetch-api, better readability, better maintainability:

{ ctx.header('Authorization', `Token ${await getToken()}`) return await next() } const useTimestamp = async (ctx, next) => { ctx.header('X-Timestamp', Date.now()) return await next() } const apiV1 = getApi('http://yourdomain.com/api/v1') // read what you see infomation-losslessly const createUser = async user => await apiV1(`user`, POST, json(user), useToken, useTimestamp) const resposne = await createUser({ firstName: 'Chun', lastName: 'Li' })">
import { getApi, json, POST } from "es-fetch-api";
import { getToken } from 'token-helper'

// read what you see
const useToken = async (ctx, next) => {
    ctx.header('Authorization', `Token ${await getToken()}`)
    return await next()
}
const useTimestamp = async (ctx, next) => {
    ctx.header('X-Timestamp', Date.now())
    return await next()
}

const apiV1 = getApi('http://yourdomain.com/api/v1')

// read what you see infomation-losslessly 
const createUser = async user => await apiV1(`user`, POST, json(user), useToken, useTimestamp)

const resposne = await createUser({
    firstName: 'Chun',
    lastName: 'Li'
})

4. To use custom middlewares for every invocation.

Using axios:

import axios from 'axios'
import { getToken } from 'token-helper'

const useToken = async () => ({ 'Authorization': `Token ${await getToken()}` })
const useTimestamp = async () => ({ 'X-Timestamp': Date.now() })

// headers is static, especially the X-Timestamp. Easy to maintain? No!
const apiV1 = axios.create({
    baseURL: 'http://yourdomain.com/api/v1',
    headers: { ...await useToken(), ...await useTimestamp() }
})

const createUser = async user => await apiV1.post({ url: `/user`, data: user, })
const getUser = async id => await apiV1.get({ url: `/user`, params: { id } })

Using es-fetch-api, better readability, better maintainability:

{ ctx.header('Authorization', `Token ${await getToken()}`) return await next() } const useTimestamp = async (ctx, next) => { ctx.header('X-Timestamp', Date.now()) return await next() } // Just append the middlewares, so easy. const apiV1 = (...args) => getApi('http://yourdomain.com/api/v1')(...args, useToken, useTimestamp) const createUser = async user => await apiV1(`user`, POST, json(user))">
import { getApi, json, POST } from "es-fetch-api";
import { getToken } from 'token-helper'

const useToken = async (ctx, next) => {
    ctx.header('Authorization', `Token ${await getToken()}`)
    return await next()
}
const useTimestamp = async (ctx, next) => {
    ctx.header('X-Timestamp', Date.now())
    return await next()
}

// Just append the middlewares, so easy.
const apiV1 = (...args) => getApi('http://yourdomain.com/api/v1')(...args, useToken, useTimestamp)

const createUser = async user => await apiV1(`user`, POST, json(user))

5. Process response.

For instance with the getUser function.

When the user exists, the response should be:

Status: 200 OK
Content-Type: application/json
Body: {ok: true, data: {"firstName": "Chun", "lastName": "Li"}}

When the user doesn't exist, the resposne should be:

Status: 404 NotFound
Content-Type: application/json
Body: {ok: false, message: 'User doesn't exist.'}

Using axios:

import axios from 'axios'

const apiV1 = axios.create({ baseURL: 'http://yourdomain.com/api/v1' })
const getUser = async id => {
    try {
        const response = await apiV1.get({ url: `/user`, params: { id } })
        console.log(response.status, response.statusText)
        // So many data and error, make me confused...don't forget write the .data after the response :)
        const { data } = response.data
        return data
    } catch (error) {
        // which error is the error i want to use?
        console.log(error.response.data.message ?? error.message)
    }
}

Using es-fetch-api, great readability:

{ try { const response = await apiV1(`user`, query({ id })) console.log(response.status, response.statusText) const { ok, data, message } = await response.json() // read what you see if (!ok) throw { message } // throw the error as you will return data } catch (error) { console.log(error.message) } }">
import { getApi, query } from "es-fetch-api";

const apiV1 = getApi('http://yourdomain.com/api/v1')

const getUser = async id => {
    try {
        const response = await apiV1(`user`, query({ id }))
        console.log(response.status, response.statusText)
        const { ok, data, message } = await response.json() // read what you see
        if (!ok) throw { message }  // throw the error as you will
        return data
    } catch (error) {
        console.log(error.message)
    }
}

6. Process responses in a unified way

Using axios:

import axios from 'axios'

// can you understand it? 
// There seems no way to process errors in a unified way?
const apiV1 = axios.create({ baseURL: 'http://yourdomain.com/api/v1' })

const getOne = async config => {
    try {
        const resposne = await apiV1(config)
        console.log(response.status, response.statusText)
        const { ok, data, message } = response.data
        return data
    } catch (error) {
        console.log(error.response.data.message ?? error.message)
    }
}

Using es-fetch-api, great readability:

{ try { const resposne = await apiV1(...args, useToken) // you can append custom middlewares here. console.log(response.status, response.statusText) const { ok, data, message } = await response.json() // read what you see if (!ok) throw { message } // throw the error as you will return data } catch (error) { console.log(error.message ?? error) } } // getOne is the unified way to process every response. You could also write other logics such as getList // read what you see const getUser = async id => getOne(`user`, query({ id }))">
import { getApi, query } from "es-fetch-api";

const apiV1 = getApi('http://yourdomain.com/api/v1')

const getOne = async (...args) => {
    try {
        const resposne = await apiV1(...args, useToken) // you can append custom middlewares here.
        console.log(response.status, response.statusText)
        const { ok, data, message } = await response.json() // read what you see
        if (!ok) throw { message }  // throw the error as you will
        return data
    } catch (error) {
        console.log(error.message ?? error)
    }
}

// getOne is the unified way to process every response. You could also write other logics such as getList
// read what you see
const getUser = async id => getOne(`user`, query({ id }))

One word reason

In es-fetch-api, each api invocation is a middlewares-chain, which means everything is extensible without introducing more complexity, no matter you want to process request and response in any unified way or case by case.

Built-in middlewares

method middleware

This middleware is used to set HTTP method, it accepts a string parameter for method name to use. If an unsupported method name is used, an exception will be thrown.

import { getApi, method } from "es-fetch-api";

const api = getApi('http://mydomain.com/api')

const response = api('/', method('DELETE'))

method aliases

GET, POST, PUT, PATCH and DELETE, these are shorthands for each corresponding method.

import { getApi, DELETE } from "es-fetch-api";

const api = getApi('http://mydomain.com/api')

const response = api('/', DELETE)

json middleware

This middleware is used to declare the HTTP request body is an JSON object.

It accepts an Object parameter to pass the body object in.

When you use this middleware, the Content-Type: application/json header will be set automatically.

import { getApi, POST, json } from "es-fetch-api";

const api = getApi('http://mydomain.com/api')

const response = api('/', POST, json({ hello, world }))

query middleware

This middleware is used to declare the query string parameters of the request URL.

It accepts two parameters.

  1. an Object, whose keys are the query parameter names and their corresponding value(s) are the query parameter values. If a value is an array with more than one element, then it will be an multi-value parameter.
  2. a Boolean, used to indicate whether each query parameter value should be appended to existed values. By Default, it's false.
import { getApi, query } from "es-fetch-api";

const api = getApi('http://mydomain.com/api?hello=1')

api(query({ hello: 'world' })) // http://mydomain.com/api?hello=world
api(query({ hello: 'world' }, true)) // http://mydomain.com/api?hello=1&hello=world
api(query({ hello: [ 'Bing', 'Dwen', 'Dwen' ], world: '2022' })) // http://mydomain.com/api?hello=Bing&hello=Dwen&hello=Dwen&world=2022

form middleware

This middleware is used to declare the HTTP request body is form data.

It accepts an Object parameter to pass form data in.

When you use this middleware, the Content-Type: application/x-www-form-urlencoded header will be set automatically.

import { getApi, form } from "es-fetch-api";

const api = getApi('http://mydomain.com/api')

api(POST, form({ hello: 'world' })) // hello=world

file middleware

This middleware is used to declare the HTTP request is uploading files.

It accepts three parameters:

  1. the name of FormData field contains the file
  2. a File object
  3. give a filename, by default it's the original filename

abortable middleware

This middleware injects AbortController::signal into fetch, so that you can abort the request as you wish.

You can use it to implement manual abort, timeout abort and so on.

When the AbortController::abort() is invoked, an exception will be thrown.

controller.abort(), 1000) api(abortable(controller))">
import { getApi, abortable } from "es-fetch-api";

const api = getApi('http://mydomain.com/api')
const controller = new AbortController()
setTimeout(() => controller.abort(), 1000)
api(abortable(controller))

Middleware development

Each middleware MUST follow the same signature, finally return await next().

const example = async (ctx, next) => {
    // TODO: your logic
    return await next()
}

More about the ctx

The ctx is completely same as the Request, except the ctx exposes a helper method used to set request headers, see useToken middleware example in this document.

License

MIT

Translations

You might also like...

Simplifies node HTTP request making.

Requestify - Simplifies node HTTP request making. Requestify is a super easy to use and extendable HTTP client for nodeJS + it supports cache (-:. Ins

Nov 28, 2022

Run HTTP over UDP with Node.js

nodejs-httpp - Run HTTP over UDP based transport and Bring Web in Peer or P2P styles main js modules: udt.js, httpp.js, udts.js and httpps.js, that's

Aug 2, 2022

Library agnostic in-process recording of http(s) requests and responses

@gr2m/http-recorder Library agnostic in-process recording of http(s) requests and responses Install npm install @gr2m/http-recorder Usage import http

May 12, 2022

make streaming http requests

hyperquest treat http requests as a streaming transport The hyperquest api is a subset of request. This module works in the browser with browserify. r

Sep 8, 2022

Wrap native HTTP requests with RFC compliant cache support

cacheable-request Wrap native HTTP requests with RFC compliant cache support RFC 7234 compliant HTTP caching for native Node.js HTTP/HTTPS requests. C

Dec 20, 2022

Global HTTP/HTTPS proxy agent configurable using environment variables.

global-agent Global HTTP/HTTPS proxy configurable using environment variables. Usage Setup proxy using global-agent/bootstrap Setup proxy using bootst

Dec 20, 2022

An HTTP Web Server for Chrome (chrome.sockets API)

An HTTP Web Server for Chrome (chrome.sockets API)

Dec 31, 2022

A fully-featured Node.js REST client built for ease-of-use and resilience

A fully-featured Node.js REST client built for ease-of-use and resilience

flashheart A fully-featured Node.js REST client built for ease-of-use and resilience flashheart is built on http-transport to provide everything you n

Jun 21, 2022

:dash: Simple yet powerful file-based mock server with recording abilities

:dash: Simple yet powerful file-based mock server with recording abilities

💨 smoke Simple yet powerful file-based mock server with recording abilities Just drop a bunch of (JSON) files in a folder and you're ready to go! Bas

Dec 13, 2022
Comments
  • Bad replace in `joinURL` function

    Bad replace in `joinURL` function

    Currently you do path.replace(/^\//, '').replace('/\/$/', '')

    the second replace has a string as the first argument that should be a regexp

    i.e. it should be path.replace(/^\//, '').replace(/\/$/, '')

    or just path.replace(/^\/|\/$/g, '') for both in one replace

    By the way, glad I stumbled on this library, was working on something very similar, but this is far more elegant. Thank you

    opened by jaromanda 2
Releases(1.0.16)
Node.js web server framework for Http/1.1 or Http/2

Node.js web server framework for Http/1.1 or Http/2 Description: This is http framework, you can use it to create Http/1.1 or Http/2 service。 Now let'

Jeremy Yu 10 Mar 24, 2022
🌐 Human-friendly and powerful HTTP request library for Node.js

Sindre's open source work is supported by the community. Special thanks to: Human-friendly and powerful HTTP request library for Node.js Moving from R

Sindre Sorhus 12.5k Jan 9, 2023
Ajax for Node.js and browsers (JS HTTP client)

superagent Small progressive client-side HTTP request library, and Node.js module with the same API, supporting many high-level HTTP client features T

Sloth 16.2k Jan 1, 2023
一个基于node.js,express,socket.io的websocket非常棒的聊天室,代码简单很适合新手. A very nice websocket chat room based on node.js, express, socket.io. the code is simple, very suitable for novices

来来往下看,虽然教程又臭又长但是一步步地保姆式教学很简单的,毕竟我是真菜鸟嘛,当然什么都往细了说╮(╯_╰)╭ 一、使用方法 该教程内容所有指令都为Linux CentOS 7.x环境下指令,其他平台请您自行查询(⊙x⊙;) 1.下载node.js并下载Sakura_Chat_Room node.j

樱樱怪 10 Jul 21, 2022
🏊🏾 Simplified HTTP request client.

Deprecated! As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time. For more i

request 25.6k Jan 4, 2023
HTTP Client Utilities

@hapi/wreck HTTP client utilities. wreck is part of the hapi ecosystem and was designed to work seamlessly with the hapi web framework and its other c

hapi.js 383 Nov 1, 2022
HTTP Client for Visual Studio Code to POST JSON, XML, image, ... files to REST APIs

friflo POST Goal Main goal of this extension is storing all HTTP request & response data automatically as files in a VSCode workspace. This ensures th

Ullrich Praetz 2 Nov 18, 2021
HTTP server mocking and expectations library for Node.js

Nock HTTP server mocking and expectations library for Node.js Nock can be used to test modules that perform HTTP requests in isolation. For instance,

Nock 11.9k Jan 3, 2023
Full-featured, middleware-oriented, programmatic HTTP and WebSocket proxy for node.js

rocky A multipurpose, full-featured, middleware-oriented and hackable HTTP/S and WebSocket proxy with powerful built-in features such as versatile rou

Tom 370 Nov 24, 2022
A full-featured http proxy for node.js

node-http-proxy node-http-proxy is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as

http ... PARTY! 13.1k Jan 3, 2023