ä¸ć–‡ | English
Very very very powerful, extensible http client for both node.js and browser.
Still using axios
? ES-Fetch-API
creates sunny world for you.
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:
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:
import { getApi } from "es-fetch-api";
import { query } from 'es-fetch-api/middelwares/query.js'
// 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)
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:
import { getApi } from "es-fetch-api";
import { json } from 'es-fetch-api/middelwares/body.js'
import { POST } from 'es-fetch-api/middelwares/methods.js'
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'
})
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:
import { getApi } from "es-fetch-api";
import { json } from 'es-fetch-api/middelwares/body.js'
import { POST } from 'es-fetch-api/middelwares/methods.js'
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'
})
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:
import { getApi } from "es-fetch-api";
import { json } from 'es-fetch-api/middelwares/body.js'
import { POST } from 'es-fetch-api/middelwares/methods.js'
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))
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:
import { getApi } from "es-fetch-api";
import { query } from 'es-fetch-api/middelwares/query.js'
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)
}
}
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:
import { getApi } from "es-fetch-api";
import { query } from 'es-fetch-api/middelwares/query.js'
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 }))
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.
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 } from "es-fetch-api";
import { method } from 'es-fetch-api/middelwares/methods.js'
const api = getApi('http://mydomain.com/api')
const response = api('/', method('DELETE'))
GET
, POST
, PUT
, PATCH
and DELETE
, these are shorthands for each corresponding method
.
import { getApi } from "es-fetch-api";
import { DELETE } from 'es-fetch-api/middelwares/methods.js'
const api = getApi('http://mydomain.com/api')
const response = api('/', DELETE)
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 } from "es-fetch-api";
import { json } from 'es-fetch-api/middelwares/body.js'
import { POST } from 'es-fetch-api/middelwares/methods.js'
const api = getApi('http://mydomain.com/api')
const response = api('/', POST, json({ hello, world }))
This middleware is used to declare the query string parameters of the request URL.
It accepts two parameters.
- 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.
- a Boolean, used to indicate whether each query parameter value should be appended to existed values. By Default,
it's
false
.
import { getApi } from "es-fetch-api";
import { query } from 'es-fetch-api/middelwares/query.js'
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
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 } from "es-fetch-api";
import { form } from 'es-fetch-api/middelwares/body.js'
const api = getApi('http://mydomain.com/api')
api(POST, form({ hello: 'world' })) // hello=world
This middleware is used to declare the HTTP request is uploading files.
It accepts three parameters:
- the name of FormData field contains the file
- a
File
object - give a filename, by default it's the original filename
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.
import { getApi } from "es-fetch-api";
import { abortable } from 'es-fetch-api/middelwares/abortable.js'
const api = getApi('http://mydomain.com/api')
const controller = new AbortController()
setTimeout(() => controller.abort(), 1000)
api(abortable(controller))
Each middleware follow the same signature (Function
and AsyncFunction
both OK) and finally return next()
.
const example = (ctx, next) => {
// TODO: your logic
return next()
}
or
const example = async (ctx, next) => {
// TODO: your logic
return next()
}
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.