Recently I found some very interesting articles related to popular npm packages (request and axios - both are simple HTTP clients) and how they are dying.
It's related to the fast-growth of JavaScript. These packages were with us for a long time, so it requires a lot of work to upgrade these packages to the newest standards. In some cases, it most likely will be better to write the library from the beginning, instead of trying to upgrade it.
The most interesting thing is that a lot of people use these libraries and a lot of systems depend on them. To be honest, our system also depends on one of them. It's really concerning because we have to find alternatives for that, but how we are sure that the next library will be not die in the next half of the year or sooner. If the system is anything bigger than a simple CRUD, then rewriting a communication library every year is not a good idea. It's really important to be prepared for the future changes and keep in mind that probably in the future, npm package which we already use can be deprecated and not maintained any more.
So, I would like to share with you our approach and how we are introducing it in our system to avoid this kind of problems in the future.
First of all, we shouldn't depend on the external packages in the most vital places in our system. We have to depend on the abstractions. What does it mean? We need to create a common clear interface which will be used in our system.
One may ask, what in the case that we only use two methods from the external library in three places in our system, do we need to create an abstraction? It depends, we have to ask ourselves a question. Do we really need this library? Maybe it will be easier to create a helper class and write your own methods? In such case, we would be independent of outside sources.
Below you can see an example of a simple HTTP client and check how we can use external packages in the background without a huge impact on the main interface and whole system. It's important that we can easily replace one package with another without inducing any breaking changes.
import Axios, { AxiosInstance } from 'axios';
import { IRequestConfig } from './request_config';
import { IResponse } from './response';
export default class RestClient {
private _client: AxiosInstance;
constructor() {
this._client = Axios.create();
}
public setBaseUrl(baseUrl: string) {
this._client.defaults.baseURL = baseUrl;
}
public async get<T = any, R = IResponse<T>>(url: string, config?: IRequestConfig): Promise<R> {
return await this._client.get(url, config);
}
public async delete<T = any, R = IResponse<T>>(url: string, config?: IRequestConfig): Promise<R> {
return await this._client.delete(url, config);
}
public async post<T = any, R = IResponse<T>>(url: string, data?: any, config?: IRequestConfig): Promise<R> {
return await this._client.post(url, data, config);
}
public async put<T = any, R = IResponse<T>>(url: string, data?: any, config?: IRequestConfig): Promise<R> {
return await this._client.put(url, data, config);
}
public async patch<T = any, R = IResponse<T>>(url: string, data?: any, config?: IRequestConfig): Promise<R> {
return await this._client.patch(url, data, config);
}
}
That's it, there's no reason to be afraid of being up-to-date with JavaScript. It's a really interesting and challenging journey. What about your approaches? How do you deal with such problems?