@awey/maxios
v2.0.10
Published
Use axios to fetch data, but configure every api with 4 levels of configurations.
Downloads
137
Readme
Maxios
Maxios is a library based on Axios for making data requests. Its main function is to layer Axios configurations and merge them using the most common logic (merge/join/replace, etc.).
Motivation
Although there are countless similar libraries on the market, I haven't encountered any that can manage various common request configurations in a layered manner during daily development. For example, when you want to handle errors, if you use Axios directly, you might use its interceptors for global error handling (e.g., error prompts). However, in some special scenarios, you might want certain requests to handle errors independently without triggering the global error logic. Or you may want to add specific error-handling logic for certain modules while ensuring that the global error logic works as expected. If you want to manage common request configurations in a layered way and control whether upper-layer configurations are effective at any time, Maxios is your best choice.
Installation and Import
You can install Maxios via Yarn or NPM:
# npm
npm install @awey/maxios
# yarn
yarn add @awey/maxios
Then, import Maxios into your code:
import { globalConfig, modulize } from '@awey/maxios'
globalConfig({
baseUrl: '/api'
})
const request = modulize({
baseUrl: '/user'
})
request().success(users => {/* Business logic */})
Best Practices
Maxios recommends organizing your requests into different model files after completing the global request configuration. Then, import the models in your business code and use the methods within them to initiate requests.
First, configure the global request:
/* global-config.ts */
import { globalConfig } from '@awey/maxios'
globalConfig({
baseUrl: '/api'
}, {
requestError (err) {/* handle error */}, // Logic for handling request errors
expect: response => response.data.code === 0, // Whether the server response meets expectations
error (data) {/* handle error */}, // Logic for handling unexpected server responses
extractor: response => response.data.data, // How to extract business data from AxiosResponse when the request is as expected
// ...other configurations
})
Then create model files to categorize requests:
/* models/user.ts */
interface User {
id: number
name: string
age: number
gender: 'male' | 'female'
}
type UserInfo = Pick<User, 'name', 'age', 'gender'>
interface ResponseData<T> {
code: number
msg: string
data: T
}
const request = modulize({
baseURL: '/user'
})
// It is recommended to expose the entire model's interface as an object
export default Object.freeze({
getUsers (condition: Partial<UserInfo>) {
return request<void, ResponseData<User[]>>({
params: condition
})
},
getUserById (id: number) {
return request<void, ResponseData<User>>({
url: `/${id}`
})
}
})
// Alternatively, each interface can be returned independently
export const createUser = (userInfo: UserInfo) => {
return request<UserInfo, ResponseData<User>>({
method: 'POST',
data: userInfo
})
}
Finally, in your business code, import the model you defined to write business logic (Maxios supports use in any framework, the example below uses React):
/** Business code */
import { useState } from 'react'
import userModel, { User } from 'model/user'
const [usersLoading, setUsersLoading] = useState(false)
const [users, setUsers] = useState<User[]>([])
// Returning the model as an object is recommended because it enhances the readability of your business code
// Also, your methods can be named more concisely with the model prefix
userModel.getUsers({ name: 'Tony'})
.loading(setUsersLoading)
.success(setUsers)
Chained Calls
The request()
function returned by modulize()
returns an object that supports chained calls. As shown in the code above, you can use methods like loading()
, success()
, etc., in a chained manner after getUsers()
, making your code more concise and elegant.
This object that supports chained calls provides the following methods for you to use:
loading
: This method accepts a callback function that is called when the loading state of the request changes; the callback function accepts aboolean
typestatus
parameter to indicate the current loading state; it can returnfalse
to prevent higher-level callback functions from executingrequestError
: This method accepts a callback function that is called when a request error occurs; the callback function accepts anAxiosError
typeerror
parameter to indicate the current error information; it can returnfalse
to prevent higher-level callback functions from executingerror
: This method accepts a callback function that is called when theexpect
in Maxios configuration returnsfalse
; it accepts adata
parameter of typeOriginResult
to indicate the original data returned by the server; it can returnfalse
to prevent higher-level callback functions from executingsuccess
: This method accepts a callback function that is called when theexpect
in Maxios configuration returnstrue
; it accepts adata
parameter of typeFinalResult
to indicate the data extracted from the server's response by theextractor
in Maxios configuration; it can returnfalse
to prevent higher-level callback functions from executinganyway
: This method accepts a callback function that is always called regardless of what happens with the request; the callback function has two parameters, the first parameterresult
represents the result of the request, which may be anAxiosResponse
or anAxiosError
, and the second parameterconfig
represents the final configuration information used for the current request; it can returnfalse
to prevent higher-level callback functions from executingabort
: This method is used to cancel the request
API Overview
Maxios provides the following APIs:
globalConfig(axiosConfig, MaxiosConfig)
: This method is used to set global request configurations, the first parameter is the configuration passed to Axios, and the second parameter is the configuration passed to Maxiosmodulize(axiosConfig, maxiosConfig)
: This method is used to obtain a modular request method, the first parameter is the configuration passed to Axios, and the second parameter is the configuration passed to Maxios, it returns arequest(axiosConfig, maxiosConfig)
methodrequest(axiosConfig, maxiosConfig)
: The method returned bymodulize()
, used to initiate requests; it accepts the same parameters asglobalConfig()
andmodulize()
, the first parameter is the configuration passed to Axios, and the second parameter is the configuration passed to Maxios; it returns a chained call object introduced earlierrace(requests)
: This method is used to make multiple requests in a race condition, using the result of the first returned request as its result; it accepts an array composed of chained objects returned by therequest()
method mentioned earlier; it also returns a chained call objectall(requests)
: This method is used to initiate multiple requests simultaneously and use the results of all requests as its result; it accepts an array composed of chained objects returned by therequest()
method mentioned earlier; it also returns a chained call object
It should be noted that to get a complete type hint experience, you need to specify specific types for its generics when calling the request
method. It accepts three generics:
Payload
: The type of payload data for the request, i.e., the type of data attached to thebody
of the requestOriginResult
: The data type of the original result returned by the requestFinalResult
: The data type of the result returned by the request after being extracted by theextractor
(theextractor
will be explained in detail later)
It is worth noting that all places that accept axiosConfig
and maxiosConfig
parameters also accept a function that returns a configuration object as a parameter. For example:
globalConfig(() => ({
baseUrl: 'api',
headers: {
token: localStorage.getItem('auth-token')
}
}))
When some configurations need to be dynamically obtained for each request, using functional configuration is a better choice.
Axios Configuration Merging Strategy
For
axiosConfig
, please refer to the Axios official website.
As mentioned earlier, you can manage Axios configuration information for the same request at different levels through different Maxios APIs. This is also the core purpose and function of Maxios.
Axios has many configuration items, but in general, Maxios follows the strategy of "the lower the level, the higher the priority" to merge configurations at each level. Specifically, it is request > modulize > globalConfig
. Most configurations in the Axios configuration object will be directly replaced (replace) by higher-priority configurations with the same name, but some special configurations use different merging strategies in Maxios.
baseURL
: ThebaseURL
configured at different levels will use the "Path Join" strategy, with higher levels in front and lower levels behind. For example, if/api
is configured inglobal
,/setting
inmodule
, and/role
inrequest
, the final request'sbaseURL
will be/api/setting/role
headers
: Theheaders
objects configured at all levels will be merged into one object before the final request through a method similar toObject.assign
params
: Theparams
objects configured at all levels will be merged into one object before the final request through a method similar toObject.assign
data
: Thedata
objects configured at all levels will be merged into one object before the final request through a method similar toObject.assign
; however, it should be noted that ifdata
is not a simple object at any level, the final request will use that configuration
Common Maxios Configurations
The second parameter maxiosConfig
of globalConfig
, modulize
, and request
represents the configuration object provided to Maxios. Maxios supports the following common configurations:
requestError: AxiosError => boolean | void
: When a request error occurs (unable to obtain response data), you may want to handle it (e.g., prompt the user that the request failed), you can userequestError
to configure a callback function for handling; the callback function accepts anAxiosError
representing the request error information as the only parameter; the chained call object mentioned earlier also provides a method with the same name, which has the same effectexpect: AxiosResponse => boolean
: A callback function used to determine whether the response returned by the request meets expectations. If it returnsfalse
, the response is considered not to meet expectations, and Maxios will call the callback function configured inerror()
below; if it returnstrue
, the response is considered to meet expectations, and the callback function configured insuccess
will be called; the callback function accepts anAxiosResponse
representing the response information as the only parametererror: OriginResult => boolean | void
: When the request returns data that does not meet expectations, if you need to handle this situation, you can useerror
to configure some callback functions for handling; the callback function accepts anOriginResult
representing the original response data (obtained from the response information object) as the only parameter; the chained call object mentioned earlier also provides a method with the same name, which has the same effectextractor: OriginResult => FinalResult
: When the request returns data that meets expectations, there may be some common structures in the outermost layer of the data, and thesuccess
processing logic may not care about these common structures. At this time, you can useextractor
to strip the outer structure and extract business data; the callback function accepts anOriginResult
parameter representing the original return data, and it needs to return the extracted business data; the default extraction logic is to directly extract the original response datasuccess: FinalResult => boolean | void
: When the request returns data that meets expectations, your subsequent business logic can usesuccess
for processing; the chained call object mentioned earlier also provides a method with the same name, which has the same effectloading: Boolean => boolean | void
: This configuration is used to indicate the loading state of the request; when the loading state of the request changes, this callback function will be called; the callback function accepts aboolean
representing whether it is currently loading as the only parameter; the chained call object mentioned earlier also provides a method with the same name, which has the same effectanyway: (AxiosResponse | AxiosError) => boolean | void
: Regardless of the result of the request, this callback function will always be called after the request ends; the callback function accepts a single parameter to represent the response information or response error information; the chained call object mentioned earlier also provides a method with the same name, which has the same effect
Priority Strategy for Common Maxios Configurations
The following configurations follow the strategy of "the lower the level, the higher the priority", and after the lower-level configuration, the same name configuration of the upper level will not take effect:
expect
extractor
The following configurations will take effect at all levels, and the execution order is from low to high; if the callback function of the lower level does not want the callback of the upper level to continue executing, it can interrupt the execution of subsequent higher-level callback functions by returning false
:
requestError
error
success
loading
anyway
It is worth noting that for these configurations (callback functions) that will be executed at all levels, there are actually four levels (the lowest level is the chained call object mentioned earlier), and these levels are from high to low:
globalConfig
modulize
request
- Chained call object
Advanced Configurations
Maxios also provides some advanced configurations. These configurations correspond to functions that are not commonly used, but once you have the corresponding needs, they will perfectly help you handle all the dirty work.
Retry Requests
Maxios provides a request retry function. You can specify conditions under which to retry requests. This feature is particularly useful in certain scenarios, such as when you start using a dual-token (Access Token / Refresh Token) mechanism to provide seamless renewal functionality for user authentication, Maxios's retry feature can come in handy.
retryWhen
: Request retry logicretryWhen.requestSuccess
: Retry logic after a successful requestretryWhen.requestSuccess.beforeRetry?: () => Promise | boolean | void
: Logic to be executed before each retry. IfPromise.reject()
orfalse
is returned, the subsequent retry logic will be stoppedretryWhen.requestSuccess.condition?: AxiosResponse => boolean
: Retry conditionretryWhen.requestSuccess.retryOthers?: boolean | 'module' | 'global'
: Whether to cancel and retry other ongoing requests during retryretryWhen.requestSuccess.maximumCount?: number
: Maximum number of retries
retryWhen.requestError
: This configuration represents the retry logic after a request failureretryWhen.requestError.beforeRetry?: () => Promise | void
: Logic to be executed before each retryretryWhen.requestError.condition?: AxiosError => boolean
: Retry conditionretryWhen.requestError.retryOthers?: boolean | 'module' | 'global'
: Whether to cancel and retry other ongoing requests during retryretryWhen.requestError.maximumCount?: number
: Maximum number of retries, default is 1
Result Caching
Maxios provides a request result caching function. If some requests return data with a very low update frequency and are requested frequently across the client, you can consider using result caching to reduce the number of real requests. The related configurations for request result caching are as follows:
cache
: Request result caching logiccache.type
: Specify the type of cache to use, which can bememory
,session
(window.sessionStorage
), orlocal
(window.localStorage
)cache.key
: Cache key, used to identify and retrieve cached results
Custom Request Method
Maxios uses axios.request()
by default to initiate requests. In some cases (such as when you want to add some Ajax probes to your application to collect performance data and error information of Ajax requests), you may want to customize the request method. You can use the maxiosConfig.request
configuration to let Maxios use the request method you provide to initiate requests. The type signature of this configuration is as follows:
type TRequest = <T = unknown, R = AxiosResponse<T>, D = any> (config: AxiosRequestConfig<D>) => Promise<R>
Migrating from V1 to V2
If you are upgrading from V1 to V2, you can check the following checklist for the changes you need to make:
global()
is renamed toglobalConfig()
, and the parameters have changed from one to two, withaxiosConfig
separated frommaxiosConfig
as the first parameter- The parameters of
modulize()
andrequest()
have changed from one to two, withaxiosConfig
separated frommaxiosConfig
as the first parameter - The callback function configuration for request errors
maxiosConfig.error
is renamed tomaxiosConfig.requestError
, and the return value for interrupting subsequent level execution has changed fromtrue
tofalse
- The function for determining whether the response meets expectations
indicator
is renamed toexpect
- The callback function configuration for unexpected response
maxiosConfig.bizError
is renamed tomaxiosConfig.error
, and the return value for interrupting subsequent level execution has changed fromtrue
tofalse
- The excution order of callback functions
loading
,success
andanyway
has been changed to 'down-to-up', and add add the ability of interruption subsuquent level execution by returnfalse
Related Tools
Additionally, Maxios offers converters based on React Hooks and Vue Composition styles, making it more convenient to use Maxios in real-world applications.