A few years back I was tasked with creating a wrapper library for a rather large, and clunky API. The wrapper needed to be user friendly, and had to hide any confusing details from the user. Today I wanted to revist this and show you just how simple it is to wrap an API with Node.js.
Dota 2 is a multiplayer online battle arena video game developed and published by Valve. They also provide an API for it that has a lot of features. By no means is their API bad or confusing, but it will still be a good example on how you could wrap any API.
In a folder called lib
, I will create an index.js
file and setup the basics of the class.
const axios = require('axios');
module.exports = class OpenDota {
constructor(apiKey) {
this.baseUrl = 'https://api.opendota.com/api';
axios.defaults.headers.common['Authorization'] = `Bearer ${apiKey}`;
}
};
Nothing crazy going on here. We have the api's base url hardcoded into the class, so it is hidden from the user. Also, we make sure to set the API key so that it is sent on every request created by axios.
If you are a little confused, I will clear this up by creating another index.js
file outside of the lib
folder,
and add the following:
const OpenDota = require('./lib/index');
const API_KEY = 'edd6xf36-38cb-4315-89ea-96a519210e96';
const dota = new OpenDota(API_KEY);
Then OpenDota API has a good amount of features, so today I am going to focus on four endpoints:
You'll probably be surprised at just how little code we need to write, so I am going to add four methods onto the class all at once.
const axios = require('axios');
module.exports = class OpenDota {
constructor(apiKey) {
this.baseUrl = 'https://api.opendota.com/api';
axios.defaults.headers.common['Authorization'] = `Bearer ${apiKey}`;
}
async proMatches() {
const { data } = await axios.get(`${this.baseUrl}/proMatches`);
return data;
}
async match(matchId) {
const { data } = await axios.get(`${this.baseUrl}/matches/${matchId}`);
return data;
}
async proPlayers() {
const { data } = await axios.get(`${this.baseUrl}/proPlayers`);
return data;
}
async player(accountId) {
const { data } = await axios.get(`${this.baseUrl}/players/${accountId}`);
return data;
}
};
For each endpoint, we create a method on the class that handles making a specific network request. We also
make sure to destructure data
from the axios response, so that part is hidden from the user.
The last step is to actually use our API wrapper! Just so it's clear, dota.proMatches
and
dota.proPlayers
will both return an array of objects.
const OpenDota = require('./lib/index');
const API_KEY = 'edd6xf36-38cb-4315-89ea-96a519210e96';
const dota = new OpenDota(API_KEY);
(async () => {
const [firstMatch] = await dota.proMatches();
const match = await dota.match(firstMatch.match_id);
const [firstPlayer] = await dota.proPlayers();
const player = await dota.player(firstPlayer.account_id);
})();
That's about it for our API wrapper folks. Of course this was a very simple example, but I believe following this approach could be scaled out well. For a real wrapper, you'd probably want to separate functionality into different classes, instead of having everything on one class like we did here. Anyways, I hope this reaches out and helps someone.
As always, if you have any problems understanding any of this, feel free to shoot me a message, and I'll make sure it's crystal clear.