An HTTP client built on fetch, offering an Axios-like experience with support for interceptors.
- Description
- Installation
- Quick Start
- Request Methods
- Creating an Instance
- Request Config
- Response Schema
- Interceptors
- Error Handling
- Using multipart/form-data
- TypeScript & ES6
- License
This is a lightweight HTTP client based on the built-in fetch, featuring a convenient syntax similar to Axios. The library supports interceptors for requests and responses, making it easier to add headers, handle errors, log requests, and manage other networking aspects.
# using npm
$ npm install @yaiuse/echo
# or using yarn
$ yarn add @yaiuse/echo
# or using bun
$ bun add @yaiuse/echoAfter installation, you can use an instance created via create to benefit from interceptors:
Note: Only instances created via
echo.create(config)support interceptors. The defaultechoinstance (created withoutcreate) does not support interceptors.
import echo from '@yaiuse/echo'
// Create an instance with base configuration and interceptors support
const echoBase = echo.create({
baseURL: 'http://localhost:4200/api',
headers: { 'Content-Type': 'application/json' }
})
// GET request with then
echoBase
.get('/users')
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
// POST request with async/await
const response = await echoBase.post('/login', {
username: 'admin',
password: '123456'
})The instance (or the default echo) supports the following methods:
request(config)get(url, options?)post(url, body?, options?)put(url, body?, options?)patch(url, body?, options?)delete(url, options?)
Where:
url— A string indicating the endpoint. IfbaseURLis set, it will be prepended unlessurlis an absolute path (e.g., starting withhttp://orhttps://, in which casebaseURLwill be ignored).body— The request body for methods that allow sending data (POST,PUT,PATCH).options— Additional configuration (headers,responseType,params, etc.).
Example of creating an instance:
import echo from '@yaiuse/echo'
// Define configuration
const config: EchoCreateConfig = {
baseURL: 'http://localhost:4200/api',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
}
// Create an Echo instance with interceptors support
const echoBase = echo.create(config)
// Use `echoBase` like `echo`
const response = await echoBase.get('/users')You can also create a minimal version of echo, acting as a simple wrapper around fetch:
const echoServer = new EchoClient(config)This can be useful for middleware requests that do not require interceptors or additional logic:
const echoServer = (
accessToken?: string,
refreshToken?: string,
): EchoClientInstance =>
new EchoClient({
...config,
headers: {
...(accessToken && { Authorization: BEARER(accessToken) }),
...(refreshToken && { Cookie: REFRESH(refreshToken) })
}
})These are the available configuration parameters for making requests:
{
// Base URL to be prepended to `url`
baseURL: 'https://api.example.com',
// The endpoint for the request
url: '/user',
// HTTP method (GET, POST, etc.)
method: 'GET',
// Request headers
headers: { 'Content-Type': 'application/json' },
// URL parameters
params: { limit: 10 },
// Expected response type (e.g., 'json' | 'text' | 'blob' | 'formData' | ...)
responseType: 'json',
// Other fields supported by fetch.
}Example:
echoBase.get('/users', { params: { limit: 10 } })A response object contains:
{
// Data returned by the server
data: {},
// HTTP status code
status: 200,
// Status message
statusText: 'OK',
// Response headers
headers: {},
// Request configuration
config: {},
// The final request instance
request: {}
}Example:
echoBase.get('/users').then(response => {
console.log(response.data)
console.log(response.status)
console.log(response.statusText)
console.log(response.headers)
console.log(response.config)
console.log(response.request)
})Interceptors let you intercept and modify requests or responses (and even errors) before they are handled by your application. They are available on instances created via echo.create(config) and can be asynchronous:
- onFulfilled: Modify the request configuration before sending.
- onRejected: Handle errors or recover from them.
const echoAuth = echo.create(config)
echoAuth.interceptors.request.use(
'auth',
// Optionally, you can pass null
config => {
// Example of Append Authorization header without overwriting other headers:
config.headers = {
...config.headers,
Authorization: 'Bearer myToken'
}
return config
},
reject => {
// Optionally handle errors during config preparation
return reject
}
)- onFulfilled: Transform or inspect successful responses.
- onRejected: Handle errors or recover from them.
echoAuth.interceptors.response.use(
'auth',
// Optionally, you can pass null
response => {
// Optionally modify response
return response
},
async reject => {
// Example of response reject handling
if (isEchoError(reject)) {
const originRequest: EchoConfig & { _isRetry?: boolean } = reject.config
const status401 = reject.response?.status === 401
const validRequest = !originRequest._isRetry && status401
// Check valid request
if (!validRequest) return reject
originRequest._isRetry = true
if (reject.message === 'Unauthorized') {
removeAccessToken()
} else if (reject.message === 'Jwt expired') {
try {
// Get new tokens
await tokenService.getNewTokens()
echoAuth.request(originRequest)
} catch {
removeAccessToken()
}
}
}
// Return error if it is not handled
// Always return error
return reject
}
)- All
onFulfilledandonRejectedhandlers execute sequentially in the order added. - Always return the error if it is not handled.
You can manage interceptors with these methods:
- Add an interceptor:
echoAuth.interceptors.request.use('uniqueKey', onFulfilled, onRejected)
echoAuth.interceptors.response.use('uniqueKey', onFulfilled, onRejected)- Remove a specific interceptor:
echoAuth.interceptors.request.eject('uniqueKey')
echoAuth.interceptors.response.eject('uniqueKey')- Clear all interceptors:
echoAuth.interceptors.request.clear()
echoAuth.interceptors.response.clear()An EchoError instance contains:
{
message: string, // Error message
config: EchoConfig, // Request configuration
request: EchoRequest, // The final request instance
response?: EchoResponse // (Optional) The response object if available
}Example:
echo.get('/user/12345').catch(error => {
console.log(error.message)
console.log(error.config)
console.log(error.request)
if (error.response) {
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
}
})When sending FormData | Blob, you do not need to set the Content-Type header manually. echo will automatically remove it so that fetch applies the appropriate header.
Echo is fully typed and is designed for JavaScript ES6 and higher.
This project is distributed under the MIT license.