Resreq
What is @devtea2026/rerum-quaerat-porro-animi?
It is a modern http client, based on fetch, because it is implemented internally using the onion model, so you can use middleware to intercept requests and responses elegantly.
Learn more
Install
Resreq targets modern browsers and Deno
pnpm install @devtea2026/rerum-quaerat-porro-animi
or
import Resreq from 'https://esm.sh/@devtea2026/rerum-quaerat-porro-animi'
If you are using a lower version of node, you may need to add some polyfill.
import fetch, { Headers, Request, Response } from 'node-fetch'
import AbortController from 'abort-controller'
globalThis.fetch = fetch
globalThis.Headers = Headers
globalThis.Request = Request
globalThis.Response = Response
globalThis.AbortController = AbortController
Documentation
Get Started
import Resreq from '@devtea2026/rerum-quaerat-porro-animi'
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com',
responseType: 'json'
})
const res = await @devtea2026/rerum-quaerat-porro-animi.request({
url: '/api/user',
method: 'GET',
params: { foo: 'bar' }
})
console.log(res)
const res = await @devtea2026/rerum-quaerat-porro-animi.get('/api/download', {
responseType: 'blob'
})
console.log(res)
Cancel request
const @devtea2026/rerum-quaerat-porro-animi = new Resreq()
const abortController = new AbortController()
@devtea2026/rerum-quaerat-porro-animi.get('https://example.com/api', {
signal: abortController.signal
})
.catch((error) => {
console.log(error)
})
abortController.abort()
Use Middlewares
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com'
})
@devtea2026/rerum-quaerat-porro-animi.use((next) => async (req) => {
try {
console.log(req)
const res = await next(req)
console.log(res)
return res
} catch (error) {
console.log(error)
throw error
}
})
const res = await @devtea2026/rerum-quaerat-porro-animi.get('/api', {
params: { foo: 'bar' }
})
console.log(res)
API
new Resreq(options?:Options)
Create a @devtea2026/rerum-quaerat-porro-animi instance and configure the global options.
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com',
timeout: 10000,
responseType: 'json',
throwHttpError: true,
onDownloadProgress(progress, chunk) {
console.log(progress, chunk)
}
})
@devtea2026/rerum-quaerat-porro-animi.request(options?:Options)
Use ''request'' to send the request and configure the options.
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com'
})
const res = await @devtea2026/rerum-quaerat-porro-animi.request({
url: '/api',
method: 'GET',
params: { foo: 'bar' },
throwHttpError: true,
onDownloadProgress(progress, chunk) {
console.log(progress, chunk)
}
})
console.log(res)
@devtea2026/rerum-quaerat-porro-animi[method](options?:Options)
Use ''method'' to send the request and configure the options
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com'
})
const res = await @devtea2026/rerum-quaerat-porro-animi.get('/api', {
params: { foo: 'bar' },
throwHttpError: true,
onDownloadProgress(progress, chunk) {
console.log(progress, chunk)
}
})
console.log(res)
@devtea2026/rerum-quaerat-porro-animi.use(middleware:Middleware)
Add headers using middleware.
import Resreq, { Middleware } from '@devtea2026/rerum-quaerat-porro-animi'
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com'
})
const setAccessToken: Middleware = (next) => (req) => {
req.headers.set('Access-Token', 'foo-bar')
return next(req)
}
@devtea2026/rerum-quaerat-porro-animi.use(setAccessToken)
await @devtea2026/rerum-quaerat-porro-animi.get('/api')
Rewriting headers using middleware.
import Resreq, { Req } from '@devtea2026/rerum-quaerat-porro-animi'
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com'
})
@devtea2026/rerum-quaerat-porro-animi.use((next) => async (req) => {
const _req = new Req(req, {
headers: {
'X-Custom-Header': 'bar'
}
})
console.log(_req.headers.get('Content-Type'))
console.log(_req.headers.get('X-Custom-Header'))
return await next(_req)
})
await @devtea2026/rerum-quaerat-porro-animi.get('/api', {
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'foo'
}
})
Req(request: Req, init: ReqInit | Request)
Res(response: Res, init: ResInit | Response)
In the middleware, use new Req()
and new Res()
to rewrite the request and response.
import Resreq, { Req, Res } from '@devtea2026/rerum-quaerat-porro-animi'
const @devtea2026/rerum-quaerat-porro-animi = new Resreq({
baseURL: 'https://example.com',
})
@devtea2026/rerum-quaerat-porro-animi.use((next) => async (req) => {
const _req = new Req(req, {
url: 'https://example.com/mock'
})
const res = await next(_req)
return new Res(res, {
body: { foo: 'bar' }
status: 200,
})
})
const res: Response = await @devtea2026/rerum-quaerat-porro-animi.get('/api')
console.log(res.status)
console.log(await res.json())
Warning
Req & Res extends from Request and Response; to create a new request and response in the middleware, use new Req()
and new Res()
.
To access the contents of the body
in the current middleware, please use Res.clone()
to ensure that the res received by the next middleware are unlocked.
Interfaces
Options
Options extends from the RequestInit type with some additional properties
interface Options extends Omit<RequestInit, 'body'> {
baseURL?: string
url?: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'PATCH'
params?: Record<string, any>
body?: BodyInit | Record<string, any>
meta?: Record<string, any>
timeout?: number
responseType?: 'json' | 'arrayBuffer' | 'blob' | 'formData' | 'text' | null | false
throwHttpError?: boolean
onDownloadProgress?: ProgressCallback
}
- baseURL: The url prefix of the request will be concatenated with the url in
@devtea2026/rerum-quaerat-porro-animi[method]()
to form a complete request address, the default value is ' '. - url: Request url, the default value is ' '.
- method:Request method, the default value is 'GET'.
- params: The params of a
@devtea2026/rerum-quaerat-porro-animi.get
request are automatically added to the url via the new URLSearchParams method. - body: Based on BodyInit, and adding
Record<string, any>
, which means you can pass object
directly, instead of JSON.stringify(object)
, which will automatically add Content-Type: application/json
request headers. - meta: The extra information that needs to be carried in the request is not really sent to the server, but it can be obtained in
Res.meta
and Req.meta
. - timeout: Specify the number of milliseconds of time before the request, if the time is exceeded the request will be aborted, the default value is 1000ms.
- responseType: Set how the response will be parsed, if not set or set to false, the Response instance will be returned, the default value is undefined.
- throwHttpError: If true, a status code outside of 200-299 will throw an error, the default value is false.
- onDownloadProgress: The download progress hook, which depends on the ReadableStream API, polyfill is required in lower versions of node.
To avoid adding complexity, new Resreq(options)
and @devtea2026/rerum-quaerat-porro-animi[method](options)
, in which 'options' are of the same type.
The options defined in new Resreq(options)
will take effect globally, @devtea2026/rerum-quaerat-porro-animi[method](options)
, will override the "global options", except for the following options which will not be overridden.
-
meta: The meta within the method will be shallow merged with the global meta.
-
headers: The headers defined in the method are merged into the global headers.
-
onDownloadProgress: Defining onDownloadProgress in a method and the global onDownloadProgress are both retained.
ReqInit
Options extends from the RequestInit type with some additional properties.
interface ReqInit extends Omit<RequestInit, 'body'> {
url?: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'PATCH'
meta?: Record<string, any>
timeout?: number
responseType?: ResponseType
throwHttpError?: boolean
body?: BodyInit | Record<string, any>
onDownloadProgress?: ProgressCallback
}
Note
That its 'headers' behave differently than 'Options.headers', which overrides the global headers.
ResInit
ResInit extends from the ResponseInit type with some additional properties.
interface ResInit extends ResponseInit {
meta?: Record<string, any>
body?: BodyInit | Record<string, any> | null
timeout?: number
responseType?: ResponseType
throwHttpError?: boolean
onDownloadProgress?: ProgressCallback
}
Middleware
The middleware must call next(req) to return a promise.
type Middleware = (next: Next) => (req: Req) => Promise<Res>
Standing on the shoulders of giants
Some of the inspiration for this project came from their.
- Axios: Promise based HTTP client for the browser and node.js.
- Ky: Tiny & elegant JavaScript HTTP client based on the browser Fetch API.
- Redux: Predictable state container for JavaScript apps.
- Koa: Next generation web framework for node.js.
License
This project is licensed under the MIT License - see the LICENSE file for details.