A small library for generating functions to make requests to the backend REST APIs for your client applications. Imagine you have an REST API written by backend team which has a lof of endpoints. You could go ahead and write ad-hoc ajax requests in "jquery" style:
$.ajax("/users")
.done(function() {
// success
})
.fail(function() {
// error
})
It will work, but there are a couple of problems here:
- URL hardcoding. What if you want to use the same request, but with different "success logic" in another place of your app? You would need to copy-paste it. And what if URL in backend API is changed, - you would need to go and replace the URL everywhere where you copy-pasted it.
- It is not composable. You have pieces of logic scattered all across the codebase. It would be better to have them in one place in some modular way, so you can have a client library for your REST API.
- Boilerplate. Even if you move them into separate modules/functions you would still have to write a lof of boilerplate code, which is exhausting and error-prone.
This library aims to solve above problems.
Repoint uses isomorphic-fetch under the hood, which requires ES6 Promise compatible polyfill. For more information refer to https://github.com/matthew-andrews/isomorphic-fetch#warnings
npm install --save repoint
npm install --save es6-promise
or any other polyfill
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const usersAPI = repoint.generate('users')
// makes GET request to http://api.example.com/v1/users
usersAPI.getCollection({})
// makes GET request to http://api.example.com/v1/users?some=1
usersAPI.getCollection({ some: 1 })
// makes GET request to http://api.example.com/v1/users/1
usersAPI.get({ id: 1 })
// makes POST request to http://api.example.com/v1/users with body { email: '[email protected]' } and ContentType: application/json
usersAPI.post({ email: '[email protected]' })
// OR
usersAPI.create({ email: '[email protected]' })
// makes PUT request to http://api.example.com/v1/users/1 with body { email: '[email protected]' } and ContentType: application/json
usersAPI.put({ id: 1, user: { email: '[email protected]' }})
// makes PATCH request to http://api.example.com/v1/users/1 with body { email: '[email protected]' } and ContentType: application/json
usersAPI.patch({ id: 1, user: { email: '[email protected]' }})
// OR
usersAPI.update({ id: 1, user: { email: '[email protected]' }})
// makes DELETE request to http://api.example.com/v1/users/1
usersAPI.delete({ id: 1 })
// OR
usersAPI.destroy({ id: 1 })
You can change idAttribute key for member URLs (get, update, destroy) by providing idAttribute
option, the default value is 'id'
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const usersAPI = repoint.generate('users', { idAttribute: 'slug' })
// makes GET request to http://api.example.com/v1/users/tom
usersAPI.get({ slug: 'tom' })
You can provide namespace as well:
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const usersAPI = repoint.generate('users', { idAttribute: 'slug', namespace: 'admin' })
// makes GET request to http://api.example.com/v1/admin/users/tom
usersAPI.get({ slug: 'tom' })
To make requests to the nested route you use nestUnder option, which expects another endpoint:
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const postsAPI = repoint.generate('posts', { nestUnder: repoint.generate('users') })
// makes GET request to http://api.example.com/v1/users/1/posts
// userId is generated by the following pattern:
// singularized endpoint name + capitalized idAttribute
// in this case, it is "user" + "Id"
postsAPI.getCollection({ userId: 1 })
// makes GET request to http://api.example.com/v1/users/1/posts/1
postsAPI.get({ userId: 1, id: 1 })
There is no limit for nesting level. idAttribute
and namespace
options also work for nested endpoints
It's often that you need to make request to a non-restful endpoint, e.g. /users/login
You can define them in the 3rd argument of generate function:
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const usersAPI = repoint.generate('users', {}, [{ method: 'post', name: 'login', on: 'collection' }])
// makes POST request to http://api.example.com/v1/users/login
usersAPI.login({ /* params */ })
Those are URLs with no collectionURL and with no idAttributes (similar to Rails resource
method in routes.rb)
You can define them in the following way:
import Repoint from 'repoint'
const repoint = new Repoint({ host: 'http://api.example.com/v1' })
const userAPI = repoint.generate('user', { singular: true })
// makes GET request to http://api.example.com/v1/user
userAPI.get({}) // no `id` to get the user
This will generate all the functions omitting getCollection
. Also no idAttributes are necessary.
Notice that you are providing a 'singular' name, (of course you can provide any name, pluralization/singularization simply is not applied in this case).
When creating a Repoint instance, there is an additional config to transform the response for all the generated endpoints. By default, it returns what comes from the server as it is, but you can provide your own. I like to use humps to convert all of the object keys to camelCase or vice versa. You can do whatever you want, the only requirement is that it should be a function.
import Repoint from 'repoint'
import { camelizeKeys, decamelizeKeys } from 'humps'
const repoint = new Repoint({
host: 'http://api.example.com/v1',
// convert query params or body params keys (in case of POST/PATCH/PUT requests) to underscore-separated
paramsTransform: (data) => decamelizeKeys(data),
beforeSuccess: (data) => camelizeKeys(data),
/* beforeError: same but for error */
/* responseHandler: response decorator that receives Response promise. Useful for handling 204 statuses */
})
const usersAPI = repoint.generate('users')
usersAPI.get({ id: 1, firstName: 'Bob' /* will be converted to { id: 1, first_name: 'Bob' } */ })
.then((data) => /* data will have all of its object keys converted to camelCase */)
There is a fetchOpts
parameter for Repoint constructor which corresponds to init param in Fetch API.
For example, if you want to send cookies with every request, you must provide credentials: 'include'
for fetchOpts.
import Repoint from 'repoint'
const repoint = new Repoint({
host: 'http://api.example.com/v1',
fetchOpts: { credentials: 'include' }
})