Clientele is a multi-language, configurable api client. Typically, creating an api client in any language requires lots of boilerplate -- clientele aims to exchange this with a tidy configuration file passed to the package on start.
- JavaScript (no official Node support, as
fetch
is required*) - Python
- Ruby
- (Coming Soon)
- Swift
- Golang
- Node
* You could define fetch as a global in node with fetch = require('node-fetch')
, but it's not officially supported.
Note: Example usage in javascript. Python & Ruby examples found in language-specific READMEs.
const clientele = require('@density/clientele');
// Describe the api client:
const configuration = {
variables: {},
resources: {
users: {
get: {
method: 'GET',
url: 'https://densityco.github.io/clientele/users/{{id}}'
},
},
},
};
// Create the api client:
const api = clientele(configuration);
// Now use it:
api.users.get({id: '1'}).then(data => {
console.log(data);
});
Variables can be defined to help modularize the config, and can be overridden in the api call if desired. This provides an easy way to set properties that are global to all requests (for example, the hostname or an authorization token).
const configuration = {
variables: {
host: 'https://densityco.github.io/clientele',
},
resources: {
users: {
get: {
method: 'GET',
url: '{{{host}}}/users/{{id}}'
},
},
},
};
// Create the api client:
const api = clientele(configuration);
// Behind the scenes, this calls `GET https://densityco.github.io/clientele/users/1`
api.users.get({id: '1'}).then(data => {
console.log(data);
});
// Behind the scenes, this calls `GET https://example.com/users/1`
api.users.get({id: '1', host: 'https://example.com'}).then(data => {
console.log(data);
});
Clientele exposes a config
function in the root that lets you override
variables at runtime:
const configuration = {
variables: {
host: 'https://densityco.github.io/clientele',
},
resources: {
users: {
get: {
method: 'GET',
url: '{{{host}}}/posts/{{id}}',
},
},
},
};
// Create the api client
const api = clientele(configuration);
// Update variables.
api.config({host: 'https://example.com'});
// Behind the scenes, this calls `GET https://example.com/users/1`
api.foo.bar.get({id: '1'}).then(data => {
console.log(data);
});
Clientele allows the user to pass in an errorFormatter
argument that can preprocess a request
error before it is raised as an error in a specific call:
const configuration = {
variables: {
host: 'https://densityco.github.io/clientele',
},
errorFormatter: response => response.text(), // Raise the body contents as text.
resources: {
users: {
get: {
method: 'GET',
url: '{{{host}}}/posts/{{id}}',
},
},
},
};
// Create the api client.
const api = clientele(configuration);
// Pretend this call raises an error. The error will be the body contents as text.
api.foo.bar.get({id: '1'}).catch(err => {
// err is the body contents as text.
});
Clientele permits passing a raw
key in any request to add custom, non-templated keys to the
parameters passed to fetch. One example as to where this would come in handy is to permit cancelling
a request:
// Create an abort controller
const controller = new AbortController();
// Pass the abort controller to the underlying fetch call using `raw`
api.users.get({
id: '1',
raw: { signal: controller.signal },
}).then(data => {
console.log(data);
});
// Cancel the fetch call
controller.abort();
The variable token
is special. If set (and it isn't overriden in a resource),
then the header Authentication: Bearer {{token}}
is added to the request.
This can be overridden manually if you don't want to send a token in a specific
request.
If there's functionality that clientele can't handle by default, it's easy to
monkey-patch things prior to exporting. For example, here's a method to return the sum of the data returned by foo
and the data returned by bar
:
var api = clientele(...);
api.total = function() {
return Promise.all([
api.foo(),
api.bar(),
]).then(([foo, bar]) => {
return foo.data + bar.data;
});
}
export default api;
Due to clientele's common configuration format, the "generating" of APIs through compilation is exciting and feasible. The beginnings of this can be found in the compiler directory, but it's not yet production-ready.
Swift, Golang, and Node are all coming soon.
Built-in support for paginated result sets is on the roadmap.