@yai-team/echo v0.2.1
Table of Contents
- Description
- Installation
- Quick Start
- Request Methods
- Creating an Instance
- Request Config
- Response Schema
- Interceptors
- Error Handling
- Using multipart/form-data
- TypeScript & ES6
- License
Description
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.
Installation
# using npm
$ npm install @yai-team/echo
# or using yarn
$ yarn add @yai-team/echo
# or using bun
$ bun add @yai-team/echo
Quick Start
After installation, you can use an instance created via create to benefit from interceptors:
Note: Only instances created via
echo.create(config)
support interceptors. The defaultecho
instance (created withoutcreate
) does not support interceptors.
import echo from '@yai-team/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'
})
Request Methods
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. IfbaseURL
is set, it will be prepended unlessurl
is an absolute path (e.g., starting withhttp://
orhttps://
, in which casebaseURL
will be ignored).body
— The request body for methods that allow sending data (POST
,PUT
,PATCH
).options
— Additional configuration (headers
,responseType
,params
, etc.).
Creating an Instance
Example of creating an instance:
import echo from '@yai-team/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 = (
refreshToken?: string,
accessToken?: string
): EchoClientInstance =>
new EchoClient({
...config,
headers: {
...(refreshToken && { Cookie: REFRESH(refreshToken) }),
...(accessToken && { Authorization: BEARER(accessToken) })
}
})
Request Config
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', // default
// Other fields supported by fetch.
}
Example:
echoBase.get('/users', { params: { limit: 10 } })
Response Schema
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:
echo.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
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:
Request Interceptors
Purpose:
- onFulfilled: Modify the request configuration before sending.
- onRejected: Handle errors or recover from them.
Execution Flow:
- All
onFulfilled
handlers execute sequentially in the order added. - If an error occurs during the request process, the
onRejected
handlers for requests are invoked in order until one recovers the error. - These handlers catch only errors related to the request setup. If none recover, the error is propagated.
Response Interceptors
Purpose:
- onFulfilled: Modify or transform the response.
- onRejected: Handle errors during response processing.
Execution Flow:
- All
onFulfilled
handlers execute sequentially. - If an error occurs during response processing, the
onRejected
handlers for responses are invoked sequentially until one recovers the error. - These handlers catch only errors related to response handling.
- If none recover, the error is thrown.
Example:
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
},
error => {
// Optionally handle errors during config preparation
return error
}
)
echoAuth.interceptors.response.use(
'auth',
// Optionally, you can pass null
response => {
// Optionally modify response
return response
},
async error => {
// Example of response error handling
if (isEchoError(error)) {
const originalConfig: EchoConfig & { _isRetry?: boolean } = error.config
// Check valid request
const validRequest =
error.response?.status === 401 &&
(error.message === 'jwt expired' ||
error.message === 'jwt must be provided')
if (!originalConfig._isRetry && validRequest) {
originalConfig._isRetry = true
try {
// Get new tokens
await tokenService.getNewTokens()
// Retry request
return await echoAuth.request(originalConfig)
} catch (err) {
if (validRequest) {
// Remove tokens if they are invalid
removeAccessToken()
}
throw err
}
}
}
// Return error if it is not handled
return error
}
)
Managing Interceptors
You can manage interceptors with these methods:
- Add an interceptor:
echoBase.interceptors.request.use('uniqueKey', onFulfilled, onRejected)
echoBase.interceptors.response.use('uniqueKey', onFulfilled, onRejected)
- Remove a specific interceptor:
echoBase.interceptors.request.eject('uniqueKey')
echoBase.interceptors.response.eject('uniqueKey')
- Clear all interceptors:
echoBase.interceptors.request.clear()
echoBase.interceptors.response.clear()
Error Handling
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)
}
})
Using multipart/form-data
When sending FormData, you do not need to set the Content-Type
header manually. echo
will automatically remove it so that fetch
applies the appropriate header.
TypeScript & ES6
Echo is fully typed and is designed for JavaScript ES6 and higher.
License
This project is distributed under the MIT license.