1.1.2 • Published 3 years ago

rest-api-builder v1.1.2

Weekly downloads
16
License
MIT
Repository
github
Last release
3 years ago

npm coverage

rest-api-builder

Simple, flexible, scalable JavaScript client for communicating with RESTful resources.

Features

  • Auto-creates routes for standard REST endpoints
  • Built-in parameter validation
  • Request library agnostic
  • Custom route schema definitions
  • Single dependency (path-to-regexp)

Purpose

This project aims to reduce the boilerplate code for interacting with RESTful server-side resources. If you're familiar with the following setup, this library is for you.

import request from 'my-request-library'

const api = {
  foo: {
    list: () => {
      return request({ method: 'get', url: '/foo/' })
    },
    retrieve: id => {
      return request({ method: 'get', url: `/foo/${id}/` })
    },
    create: data => {
      return request({ method: 'post', url: '/foo/', data })
    },
    update: (id, data) => {
      return request({ method: 'put', url: `/foo/${id}/`, data })
    },
    destroy: id => {
      return request({ method: 'delete', url: `/foo/${id}/` })
    }
  }
}
export default api

Install

Install with npm or yarn.

npm install rest-api-builder
yarn add rest-api-builder

Usage

Create a new instance of the builder. Configuration options will be shared by all routes.

import request from 'my-request-library'
import RestAPIBuilder from 'rest-api-builder'

const config = {
  requestFn: request,
  baseURL: '',
  appendSlash: true
}
export const builder = new RestAPIBuilder(config)

Builder Config

These are the available config options for the builder. Only requestFn is required.

  • requestFn: (required) Function that will be called when you use the routes generated by the builder. Returns a promise.
  • baseURL: (optional) Value will be prepended to each path generated by the builder.
  • appendSlash: (optional) If set, / will be appended to each path. Default is true.

requestFn

requestFn should wrap your request library and accept a single parameter, the config object. Here's an example with the popular axios library:

import axios from 'axios'
import RestAPIBuilder from 'rest-api-builder'

const builder = new RestAPIBuilder({
  requestFn: config => {
    return axios(config)
  }
})

Request Config

Here's a list of the requestFn config options.

{
  // server url
  url: '/foo',

  // http method
  method: 'get',

  // data to be sent as the request body
  // only valid for POST, PUT, PATCH
  data: {
    foo: 'bar'
  },

  // any additional properties you'd like to pass to
  // your request library, eg. url parameters
  ...{
    params: {
      q: 'foo'
    }
  }
}

Most http clients will have a similar api for making requests, so it should be pretty easy to transform these values to meet your needs.

Route building

Use the builder to generate api endpoints using create(), which accepts two parameters:

  • path: (required) Path to the api resource
  • endpoints: (optional) An array of endpoint objects
import { builder } from 'path/to/builder'

const resource = builder.create({
  path: 'foo',
  endpoints: [
    { action: 'list' },
    { action: 'retrieve' },
    { action: 'create' },
    { action: 'update' }
  ]
})

This results in the following schema, where action becomes the invocation method name.

{
  list: ([config]) => { ... },                // GET /foo
  retrieve: (id[, config]) => { ... },        // GET /foo/:id
  create: (id, data[, config]) => { ... },    // POST /foo
  update: (id, data[, config]) => { ... }     // PUT /foo/:id
}

If the endpoints param is omitted, the following defaults will be created for you:

ActionHTTP MethodPath
listGET/foo
retrieveGET/foo/:id
createPOST/foo
updatePUT/foo/:id
partialUpdatePATCH/foo/:id
destroyDELETE/foo/:id

Alternatively, you can create custom endpoints by providing an endpoint schema as follows.

{
  action: String,      // invocation method name
  method: String,      // http verb
  path: String         // custom route path
}

(Note: the default route actions are reserved; if you use one in a custom endpoint configuration the other params will be ignored and the standard REST endpoint will be created instead.)

Endpoint definitions can be mixed. For example, if you want to create default endpoints for fetching data in addition to a custom route:

const resource = builder.create({
  path: 'foo',
  endpoints: [
    { action: 'list' },
    { action: 'retrieve' },
    { action: 'custom', method: 'put', path: 'bar/:id' }
  ]
})

It will create a schema that looks like this:

{
  list: ([config]) = { ... },             // GET /foo
  retrieve: (id[, config]) => { ... }     // GET /foo/:id
  custom: (id[, config]) => { ... }       // PUT /foo/bar/:id
}

Named segments begin with :. If the custom route contains a named segment, id will become a required field.

Multiple named segments are supported within a single endpoint path.

const resource = builder.create({
  path: 'foo',
  endpoints: [
    { action: 'custom', method: 'get', path: 'bar/:p1/baz/:p2' }
  ]
})

When using multiple named segments, the id argument becomes an object instead of a primitive. Each segment should map to a key in this object.

resource.custom({ p1: 'user', p2: 5 })   // PUT /foo/bar/user/baz/5

Invocation

Once the api schema has been created, you can import it and use as needed.

// src/services/api/users.js
import { builder } from './builder.js'

export const UserAPI = builder.create({
  path: 'users',
  endpoints: [
    { action: 'list' },
    { action: 'retrieve' },
    { action: 'create' }
  ]
})
// src/views/someView.js
import { UserAPI } from 'src/services/api/users.js'

async function listUsers () {
  const response = await UserAPI.users.list()
}

async function retrieveUser(userId) {
  const response = await UserAPI.users.retrieve(userId)
}

async function createUser() {
  const payload = { username: 'Foo', email: 'foo@bar.com' }
  const response = await UserAPI.users.create(payload)
}

The invocation method signature will be different depending on its usage.

Routes which require a resource identifier (id)

resource.actionWithId(id[, data, config])

An id wil be required for retrieve, update, partialUpdate, destroy, or if a custom action is used which contains at least one named segment in the path. If multiple named segments are defined, this argument should be an object whose keys map to each segment.

const resource = builder.create({
  path: 'users',
  endpoints: [
    { action: 'retrieve' },
    { action: 'custom', method: 'get', path: 'bar/:p1/baz/:p2' }
  ]
})
resource.retrieve(4)
resource.custom({ p1: 'user', p2: 5 })

Routes which require a request payload (data)

resource.actionWithData([id, ]data[, config])

A payload will be required for post, put, and patch.

const resource = builder.create({
  path: 'users',
  endpoints: [
    { action: 'create' },
    { action: 'update' }
  ]
})
resource.create({ foo: 'bar' })
resource.update(4, { foo: 'bar' })

Validation

The builder will handle basic parameter validation for the following:

  • Routes which target a specific resource
  • Routes which require a payload (post, put, patch)
const resource = builder.create({
  path: 'foo',
  endpoints: [
    { action: 'custom', method: 'put', path: 'bar/:id' }
  ]
})
resource.custom()        // throws MissingIdError
resource.custom(5)       // throws MissingPayloadError
resource.custom(5, {})   // works

Development

You'll need node.js installed.

To get started, clone the repo:

git clone https://github.com/CBMasri/rest-api-builder.git
cd rest-api-builder

Commands:

npm run test  # run tests
npm run test:coverage  # generate coverage report
npm run build  # build for production using webpack

Similar libraries

1.1.2

3 years ago

1.1.0

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago