APIClient
A really basic client to work with an API endpoints requests.
Nowadays there's almost no app that doesn't make requests to one or more external APIs, that's why I built this service.
Now, the reason this is marked as 'shared' it's because it basically organizes the API configuration and prepares the requests, but you have to give it a fetch
client to make the request. It's 'shared' because on a browser you can give it the native fetch
function, and on Node, something like node-fetch
or axios
, but that supports the native fetch
signature.
Examples
This example will be on Node, so I'll use
node-fetch
as fetch client.
First, let's start by creating the configuration:
const url = 'https://my-api.com';
const endpoints: {
login: 'auth/login',
profile: 'users/:userId',
users: 'users',
};
const fetchClient = require('node-fetch');
You need those three things to instantiate the client:
- An entry point for your API.
- A dictionary of endpoints (Check the features section to see all the possible ways to define an endpoint).
- A fetch client to make the requests.
Now, to instantiate the client:
const { APIClient } = require('wootils/shared');
const client = new APIClient(url, endpoints, fetchClient);
To make a request, you first need to generate an endpoint and use it to call the method of the type of request you want to make.
Let's say you want to authenticate a user, which is a POST
request to the login
endpoint with a body that includes username
and password
:
client.post(client.endpoint('login'), {
username: 'Rosario',
password: 'charito',
})
.then((response) => {
// Do something...
});
Ok, that one was easy, no extra options or parameters. Next will request some user information from the profile
endpoint, a GET
request that requires a userId
parameter:
client.get(client.endpoint('profile', { userId: 2509 }), {
username: 'Rosario',
password: 'charito',
})
.then((response) => {
// Do something...
});
Easy enough, right? Well, for the last example, we'll make a request to get the users directory to the users
endpoint, a GET
request and we'll include a query string parameter to "limit the number of returned users":
client.get(client.endpoint('users', { count: 10 }), {
username: 'Rosario',
password: 'charito',
})
.then((response) => {
// Do something...
});
Done! If the parameter doesn't have a placeholder on the endpoint path, it gets automatically added on the query string.
Features
Configurable endpoints
On the example above, all the endpoints were strings, but you can define them as objects to include default query string parameters, or even as sub collections of endpoints:
Endpoint objects
const endpoints = {
users: {
path: 'users',
query: {
count: 10,
offset: null,
},
},
};
In that case, if count
is not specified on endpoint(...)
, it will have a default value of 10
.
And if offset
is not specified, it won't be sent. The reason the configuration supports "nullable" parameters is so they can be used like some sort of documentation for the endpoint: "It's not used, but there's an optional parameter called ...".
Sub collections of endpoints
const endpoints = {
login: 'auth/login',
users: {
profile: 'users/:userId',
directory: {
path: 'users',
query: {
count: 10,
},
},
},
};
As you can see, both users related endpoints are now under users
, and login
is still on the top level. To access "sub endpoints" you use dot notation:
const endpointURL = client.endpoint('users.profile', { userId: 2509 });
This allows you to organize the scopes of your endpoints and make the configuration easier to read.
Built-in request methods
On the example above we saw only .get
and .post
, but the client comes with these already built-in request methods:
.get(url, options = {})
.head(url, options = {})
.post(url, body, options = {})
.put(url, body, options = {})
.patch(url, body, options = {})
.delete(url, body = {}, options = {})
If you are wondering what options
are, well, they are extra options for the fetch
client call. It can include headers, another method, another body, etc. Everything that could send on the fetch
call second parameter.
Default headers
The client allows you to set a dictionary of default headers you want to include on every request.
For example, let's say you are on a development environment and you want all your request to go out with the header x-development
set to true
:
client.setDefaultHeaders({
'x-development': true,
});
Now, all the outgoing requests will include that header.
Authorization token
If you are working with an API that requires authorization on every requests, and that provides you with a bearer token when you authenticate, you could set it on the client and it will automatically include the Authorization
header on every request and send the token:
client.setAuthorizationToken('some-token');
Done, all the requests will include Authorization: Bearer some-token
.
ES Modules
If you are using ESM, you can import the class from the /esm
sub path:
import APIClient from 'wootils/esm/shared/apiClient';
// or
import { APIClient } from 'wootils/esm/shared';
Technical documentation
- Class:
APIClient
If you are reading this form the markdown document, you can go to the online version; or you can generate the documentation site yourself by running the
docs
command:# You can either use npm or yarn, it doesn't matter npm run docs && open ./docs/index.html;