— js, axios, architecture, api — 2 min read
Thanks to axios we gain lots of power when it comes to executing and consuming REST API, but it don't gives us any "ready to use" pattern how to use and structure them in correct way.
I want to share with you my approach about how do I organize and implement services while using axios library.
In this post, I'm not going to cover basics about using axios services, and I assume you have got basic knowledge about axios and ES6 at least.
The goal will be to create API service files structure which would fulfil the criteria:
For this articule purpose I will use fake online REST API - https://jsonplaceholder.typicode.com
In first step we create simple function that will return our axios instance, so that we could use it independently on each level of our structure.
1import axios from 'axios';23const defaultOptions = {};45function axiosProvider(baseUrl, options) {6 console.log('creating axios instance')7 return axios.create({8 baseURL: baseUrl,9 ...defaultOptions,10 ...options11 });12}1314export default axiosProvider;
defaultOptions
variable is empty for now, but keep in mind it's nice place to set our defaults options for axios.
Next step is to create our basic class that we will be using across all of our services.
1import axiosProvider from './axiosProvider';23class CoreApi {4 constructor(baseUrl, slug = '') {5 this.baseUrl = baseUrl;6 this.slug = slug;7 this.api = axiosProvider(`${this.baseUrl}${this.slug}`);8 this.setInterceptors({9 beforeRequest: this._beforeRequest,10 requestError: this._requestError,11 afterResponse: this._afterResponse,12 responseError: this._responseError,13 });14 };1516 setInterceptors = ({17 beforeRequest,18 requestError,19 afterResponse,20 responseError,21 }) => {22 this.api.interceptors.request.use(beforeRequest, requestError);23 this.api.interceptors.response.use(afterResponse, responseError);24 };25 ...
In a constructor, we pass two parameters:
baseUrl
- later on, we would pass here service main url like i.e - 'https://jsonplaceholder.typicode.com'slug
- will be like cluster under which we will group our requests -> ie. '/posts'. So for /posts
would could have many requests like GET /posts
POST /posts
but also GET /posts/search?userId=1
We are using both this parameters, to create axios instance with default baseUrl, so that later on we could use it just like this.api.get('')
or this.api.get('/search?userId=' + id)
In line number 8, we are also setting interceptors that are defined underneath our constructor. It's implementation details is not crucial for now, se we skipp this.
But what important to mention, is that this interceptors will be global. What that mean is any services that we would create later on, would be using/invoking this interceptors in request cycle.
We are almost there. There is one more class that we should create to keep it flexible and extensible. This class would help us to use many APIs in our project, but still sharing some functionality between them ( ie. handling errors, share tokens, etc.)
It's basic implementation could looks something like this:
1import CoreApi from '../CoreApi';2import config from '../../config';34class PlaceholderApiProvider extends CoreApi {5 constructor(endpoint) {6 super(config.placeholderApiUrl, endpoint);7 }8}910export default placeholderApiProvider;
In our application we could have many independent API, each on different url, that we have to working on ( ie. authorization, weather, currency, cms ) . The goal here, is to create class that would be used later on to create each of our services related to particular API.
So here we just invoking CoreApi
constructor with appropriate apiUrl from config -> config.placeholderApiUrl
. In our case this value is https://jsonplaceholder.typicode.com
Keep in mind, that we could set interceptors for this particular API, just by invoking this.setInterceptors({ ... })
in the constructor. It would invoke independently from our global interceptors from CoreApi
file.
Ok, now we are ready to go with service itself. So we create class based on placeholderApiProvider
. It will be responsible to manage bunch of endpoints related to particular resources ( ie. /posts
).
1import PlaceholderApiProvider from '../serviceProviders/placeholderApiProvider';23class PostsService extends PlaceholderApiProvider {4 async getAll() {5 return this.api.get();6 }78 async getById(id) {9 return this.api.get(`/${id}`);10 }1112 async getCommentsForPost(id) {13 return this.api.get(`/${id}/comments`);14 }1516}1718const postsService = new PostsService('/posts');1920export default postsService;21export { PostsService };
So this is our goal service. We implement methods like getAll
getById
getCommentsForPost
that we could use later on in our Sagas or Components.
You can find working example based on react and create-react-app:
https://github.com/miedziak/axios-services-architecture-example