A small library that creates a pre-configured instance of axios to make HTTP requests to REST resources. Written in Typescript. Heavily inspired by AngularJS' $resource.
npm i axios-rest-resource axios
-
Create axios-rest-resource module in your utils folder
// utils/axios-rest-resource.ts import { ResourceBuilder } from "axios-rest-resource"; export const resourceBuilder = new ResourceBuilder({ baseUrl: "http://localhost:3000" });
-
Using a newly created resource builder create an actual resource
// api/entity1.js import { resourceBuilder } from "utils/axios-rest-resource"; export const entity1Resource = resourceBuilder.build({ url: "/entity1" }); // exports an object // { // create: (action, requestConfig) => axiosPromise // sends POST http://localhost:3000/entity1, // read: (action, requestConfig) => axiosPromise // sends GET http://localhost:3000/entity1, // readOne: (action, requestConfig) => axiosPromise // sends GET http://localhost:3000/entity1/{id}, // remove: (action, requestConfig) => axiosPromise // sends DELETE http://localhost:3000/entity1/{id}, // update: (action, requestConfig) => axiosPromise // sends PUT http://localhost:3000/entity1/{id} // }
-
Use your resource in your saga
import { entity1Resource } from "api/entity1"; // action here is { type: 'ENTITY1_READ_INIT' } export function* entity1ReadSaga(action) { const res = yield call([entity1Resource, entity1Resource.read], action); // sends GET http://localhost:3000/entity1 yield put({ type: "ENTITY1_READ_SUCCESS", payload: res }); } // action here is { type: 'ENTITY1_READ_ONE_INIT', meta: { id: '123'} } export function* entity1ReadOneSaga(action) { const res = yield call([entity1Resource, entity1Resource.readOne], action, { params: { id: action.meta.id } }); // sends GET http://localhost:3000/entity1/123 yield put({ type: "ENTITY1_READ_ONE_SUCCESS", payload: res }); }
You can pass an optional array of request interceptors to ResourceBuilder's constructor
export const resourceBuilder = new ResourceBuilder({
baseUrl: "http://localhost:3000",
interceptors: [myRequestInterceptor]
});
You can read more about interceptors here. The only difference with axios' original interceptors is that axios-rest-resource passes an extended version of AxiosRequestConfig to its interceptors. It has an additional property AxiosResourceAdditionalProps. You can read more about it here. It contains an object with an action that triggerred that request. Most of the time you do not need to worry about it unless you want to access that action's data.
Axios-recource exposes two pre-defined interceptors:
interceptorUrlFormatter is always applied. interceptorAuthorizationToken you have to apply manually if you want to.
You can do it like this:
import {
ResourceBuilder,
interceptorAuthorizationToken
} from "axios-rest-resource";
export const resourceBuilder = new ResourceBuilder({
baseUrl: "http://localhost:3000",
interceptors: [interceptorAuthorizationToken]
});
Here's how you can create an interceptor that logs all requests adn apply it:
import { ResourceBuilder } from "axios-rest-resource";
import { AxiosRequestConfig } from "axios";
const interceptorLog = (config: AxiosRequestConfig) => {
console.log(
`axios-rest-resource.interceptorLog -> request ${JSON.stringify(config)}`
);
return config;
};
export const resourceBuilder = new ResourceBuilder({
baseUrl: "http://localhost:3000",
interceptors: [interceptorAuthorizationToken]
});
If you want to access that additional property AxiosResourceAdditionalProps, you can do this:
import {
ResourceBuilder,
IAxiosResourceRequestConfig,
AxiosResourceAdditionalProps
} from "axios-rest-resource";
import { AxiosRequestConfig } from "axios";
const interceptorLogAction = (config: AxiosRequestConfig) => {
const configExtended = config as IAxiosResourceRequestConfig;
console.log(
`axios-rest-resource.interceptorLogAction -> action ${JSON.stringify(
configExtended[AxiosResourceAdditionalProps].action
)}`
);
return configExtended;
};
export const resourceBuilder = new ResourceBuilder({
baseUrl: "http://localhost:3000",
interceptors: [interceptorAuthorizationToken, interceptorLogAction]
});
You can pass a custom axios instance factory to ResourceBuilder. It's useful if you want to do something more with your axios instance but assign 'baseUrl' and add request inerceptors.
import { ResourceBuilder } from "axios-rest-resource";
import axios, { AxiosInstance } from "axios";
const createAxiosInstanceFromUrl = (resourceUrl: string): AxiosInstance => {
const axiosInstance = axios.create({ baseUrl: "http://localshot:3000" });
// This time we want to add response interceptors
axiosInstance.interceptors.response.use(myResponeInterceptor);
// Don't forget to add interceptorUrlFormatter if you want to keep {token} replacement in urls
axiosInstance.interceptors.request.use(interceptorUrlFormatter);
// Don't forget to append resourceUrl to baseUrl
axiosInstance.defaults.baseURL += resourceUrl;
return axiosInstance;
};
export const resourceBuilder = new ResourceBuilder(createAxiosInstanceFromUrl);
As you can see there's a lot you have to remember. Not to keep all those things in mind you utilize createAxiosResourceFactory.
import {
ResourceBuilder,
createAxiosResourceFactory
} from "axios-rest-resource";
import { AxiosInstance } from "axios";
const createAxiosResource = createAxiosResourceFactory({
baseUrl: "http://localshot:3000"
});
const createAxiosInstanceFromUrl = (resourceUrl: string): AxiosInstance => {
// Creates an axios instance with appended resourceUrl and applied interceptorUrlFormatter. You can pass an additional array of request interceptors just like with ResourceBuilder. In fact ResourceBuilder uses this very function uner the hood.
const axiosInstance = createAxiosResource(resourceUrl);
// Add that response interceptor
axiosInstance.interceptors.response.use(myResponeInterceptor);
return axiosInstance;
};
export const resourceBuilder = new ResourceBuilder(createAxiosInstanceFromUrl);