1.0.0-alpha.7 • Published 5 years ago

r3shaper v1.0.0-alpha.7

Weekly downloads
4
License
MIT
Repository
github
Last release
5 years ago

Motivation

Keeping the API layer isolated and organized can be a real pain. 😩

Where can I ergonomically normalize my requests and responses? What if tomorrow I can't or don't want to use the same request library I've used before? How can I reuse my API layer on another JS platform without depending on a request library? Are my requests isolated and testable? Can I intercept and debug all of my requests and responses?

Solution

r3shaper is a tiny tool that'll help you organize and isolate your API layer, as well as normalize your requests and responses. It provides an abstract client that can be configured to work with any request library. Effortlessly reuse your API resources on other JS platforms.

Installation

npm install r3shaper --save

Client Initialization

The client receives a configuration object with 3 options:

OptionTypeDescription
basePath (optional)stringAPI basepath.
headers (optional)objectAPI headers.
apiProviderfunction({ body?, path, method, headers?, params?, meta? }, onError, onSuccess)Wrapper function around the request library.

In order to start making requests, we have to initialize a r3shaper client and configure it to use our preferred request library. In the following example we'll use Axios:

import axios from 'axios';
import { Client } from 'r3shaper';

const apiClient = new Client({
  basePath: 'https://reqres.in',
  headers: {
    'Content-Type': 'application/json',
  },
  apiProvider: ({ body, path, headers, method, meta = {} }, onError, onSuccess) =>
    axios({
      url: path,
      data: body,
      headers,
      method,
    })
      .then(({ data }) => {
        if (meta.notification) {
          alert(meta.notification);
        }

        onSuccess(data);
      })
      .catch(onError),
});

export default apiClient;

Note: you can instantiate as many clients as you'd like.

Resources

Once we've initialized a client, it exposes us a list of 8 methods for resources creation.

  • apiClient.get()

  • apiClient.head()

  • apiClient.post()

  • apiClient.put()

  • apiClient.delete()

  • apiClient.connect()

  • apiClient.options()

  • apiClient.trace()

  • apiClient.patch()

Each of these methods receive 2 parameters:

ParameterTypeDescription
pathstringAPI endpoint. Use brackets for parameters: /user/{id}.
interceptors (optional){ onRequest?, onResponse? }An object with request and response normalizing functions.

Now we can import our new client and define our resources.

import apiClient from './apiClient';

const UserResource = {
  /**
   * Get the list of users.
   */
  index: apiClient.get('/api/users', {
    // Normalize the response
    onResponse: (data, meta) =>
      data.map(({ first_name, last_name }) => ({
        firstName: first_name,
        lastName: last_name,
      })),
  }),
  /**
   * Get a single user.
   */
  show: apiClient.get('/api/users/{id}', {
    // Normalize the response
    onResponse: ({ first_name, last_name }, meta) => ({
      firstName: first_name,
      lastName: last_name,
    }),
  }),
  /**
   * Store a new user.
   */
  store: apiClient.post('/api/users', {
    // Normalize the request
    onRequest: ({ firstName, lastName, job }, meta) => ({
      full_name: `${firstName} ${lastName}`,
      job,
    }),
  }),
  /**
   * Update a user.
   */
  update: apiClient.patch('/api/users/{id}', {
    // Normalize the request
    onRequest: ({ firstName, lastName, job }, meta) => ({
      full_name: `${firstName} ${lastName}`,
      job,
    }),
  }),
  /**
   * Delete a user.
   */
  destroy: apiClient.delete('/api/users/{id}'),
};

Resource Usage

Resources return a Promise and receive a configuration object with 4 options:

OptionTypeDescription
body (optional)objectRequest body that will go through onRequest normalizer.
params (optional)objectRoute params that will be replaced in the URL.
queryParams (optional)objectRequest URL query params.
headers (optional)objectRequest headers.
meta (optional)objectAdditional data (anything you'd like to use later).

The returned result by this Promise is the API response that went through onResponse normalizer.

import UserResource from './UserResource';

UserResource.index().then(users => {
  /* ... */
});

UserResource.show({
  params: {
    id: 1,
  },
}).then(user => {
  /* ... */
});

UserResource.store({
  body: {
    firstName: 'John',
    lastName: 'Doe',
    job: 'Procrastinator',
  },
  meta: {
    notification: 'User has been created',
  },
}).then(user => {
  /* ... */
});

UserResource.update({
  params: {
    id: 1,
  },
  body: {
    firstName: 'John',
    lastName: 'Doe',
    job: 'Procrastinator',
  },
  meta: {
    notification: 'User 1 has been updated',
  },
}).then(user => {
  /* ... */
});

UserResource.delete({
  params: {
    id: 1,
  },
  meta: {
    notification: 'User 1 has been deleted',
  },
});

Dependencies

None.

Credits

Created by Sergiu Masurceac and Stratulat Alexandru.

1.0.0-alpha.9

5 years ago

1.0.0

5 years ago

1.0.0-alpha.8

5 years ago

1.0.0-alpha.7

5 years ago

1.0.0-alpha.5

5 years ago

1.0.0-alpha.4

5 years ago

1.0.0-alpha.3

5 years ago

1.0.0-alpha.2

5 years ago

1.0.0-alpha.1

5 years ago

1.0.0-alpha.0

5 years ago

0.1.6

6 years ago

0.1.5

6 years ago

0.1.4

6 years ago

0.1.3

6 years ago

0.1.2

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago