0.2.1 • Published 5 months ago

@yai-team/echo v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

GitHub Repo npm Package npm Downloads Install Size Bundle Size Build Status Coverage Status Snyk Security License

Table of Contents

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 default echo instance (created without create) 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. If baseURL is set, it will be prepended unless url is an absolute path (e.g., starting with http:// or https://, in which case baseURL 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.

0.2.1

5 months ago

0.2.0

5 months ago

0.1.2

5 months ago

0.1.1

5 months ago

0.10.1

5 months ago

0.10.0

5 months ago

0.1.0

5 months ago

0.0.9

5 months ago

0.0.8

5 months ago

0.0.7

5 months ago

0.0.6

5 months ago

0.0.5

5 months ago

0.0.4

5 months ago

0.0.3

5 months ago

0.0.2

5 months ago

0.0.1

5 months ago