SkillAgentSearch skills...

Crudl

Abstracts and handles common JSON RESTful APIs requests and responses

Install / Use

/learn @meucredere/Crudl
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

crudl

Abstracts and handles common JSON RESTful APIs requests and responses

npm version

What is it?

crudl's goal is to abstract the most common RESTful requests and basic data handling of every frontend developer's everyday coding.

It currently has adapters for Vuex (@crudl/vuex-adapter) and Redux (@crudl/redux-dapter) - more as a way of portraying its portability and provide sample code than "officially" supporting them, as crudl exposes its main getters for easily integrating with other JavaScript frameworks or vanillaing your way out of them.

What does it do?

crudl provides an extendable and highly customizable set of operation handlers for create, read, update, delete and list requests.

It handles things like initial state schema, requests (actions in both Vuex and Redux), modifiers (mutations in Vuex, reducers in Redux), key pluralization and snake_casing on request responses, error normalization, among other useful things so you can have entire working RESTful modules with just a few lines of JavaScript:

import CRUDL from 'crudl';
import adapter from '@crudl/vuex-adapter';
// import adapter from '@crudl/redux-adapter';

export default adapter(new CRUDL('blogPost'));

And that's it! You're good to go.

crudl is not only lightweight (~3kb core, <1kb adapters, gziped), but it also helps you save tons of lines of code in modules or slices.

Installation

npm install crudl
npm install @crudl/vuex-adapter
# or npm install @crudl/redux-adapter

Data handling

Data follows this format:

  • create, read, update and delete all have the same format:
{
  item: { id: 123, foo: 'x' },
  loading: false, // boolean
  failure: null, // HTTP client `Error` or request JSON response
  config: {} // crudl's internal request config
}
  • list, on the other hand, has its exclusive format:
{
  items: {
    123: { id: 123, foo: 'x' },
    456: { id: 456, foo: 'y' }
  },
  loading: false,
  failure: null,
  config: {}
}

Data (state) can be accessed using the crudl module's default key:

state
  -> module_key
    -> [create|read|update|delete|list]
      -> [item|items|loading|failure|config]

To illustrate that using frameworks methods:

  • Redux -> store.getState().blogPost.list.loading
  • Vuex -> store.state.blogPost.list.loading

It is important to note that crudl as it is does not store data at all, but only offers you a initial state and handling and processing of JSON responses, leaving the data storage and versioning to the frameworks working around it (like Vuex and Redux).

Customization

By default, crudl expects target APIs to follow some RESTful patterns, like:

For endpoints:

  • Create -> POST -> /blog_posts
  • Read -> GET -> /blog_posts/:id
  • Update -> PUT -> /blog_posts/:id
  • Delete -> DELETE -> /blog_posts/:id
  • List -> GET -> /blog_posts

But custom ones can be provided:

new CRUDL('blogPost', {
  methods: {
    delete: 'get',
    update: 'patch'
  },
  endpoints: {
    read: '/blogs/:category_id/posts/:id/reviewed'
  }
});

Responses are also expected to follow RESTful patterns:

  • Single items: { blog_post: { id: 1, foo: 'x' } }
  • Multiple items: { blog_posts: [{ id: 1, foo: 'x' }, { id: 2, foo: 'y' }] }

The snake_casing and pluralization on endpoints and JSON keys on responses are handled by crudl behind the scenes. No need to worry about that.

It can also be customized to handle responses like this one:

  • Read: { custom_key: { id: 123, foo: 'bar' } }
  • Update: { id: 123, foo: 'bar' } (keyless)

Simply by doing this:

new CRUDL('blogPost', {
  keys: {
    read: 'custom_key',
    update: '' // keyless response
  }
});

You can find more custom settings on the "basic, advanced and expert customizations" ahead.

HTTP client

crudl requires an HTTP client by default. axios was used for its development, so it is the one recomended. Any client should work, though, as long as it follows these two rules:

1. Fires requests using the following format:

client('/url/here', {
  method: 'get, post, ...',
  data: {} // params: {} on GET requests
});

2. Responds using the following formats:

  • { data: API_SUCCESS_JSON_OBJECT } on success
  • Error { response: { data: API_ERROR_JSON_OBJECT } } on failure

You can set the default HTTP client on a global basis:

CRUDL.client = axiosClient;

Or by module:

new CRUDL('blogPost', {
  client: axiosClient
});

URL parsing (endpoints)

Request payload options

  • Redux -> dispatch(blogPost.actions.read(payload))
  • Vuex -> dispatch('blogPost/read', payload)

An endpoint like this /blogs/:category_id/posts/:id/reviewed requires that category_id and id are present on the payload. To illustrate that, a dispatch like this:

dispatch('blogPost/read', {
  category_id: 1,
  id: 2
});

Would result in a request fired to /blogs/1/posts/2/reviewed.

Additional parameters are allowed, of course, and crudl takes case of excluding endpoint parameters from the final query (for GET requests) or request body, so you don't end up with repeating parameters like /blogs/1/posts/2/reviewed?category_id=1&post=2. To illustrate that, a dispatch like this:

dispatch('blogPost/read', {
  category_id: 1,
  id: 2,
  read: false,
  reactions: true
});

Would result in a request fired to /blogs/1/posts/2/reviewed?read=false&reactions=true.

Internal crudl request options

Request payload options

  • Redux -> dispatch(blogPost.actions.read(payload))
  • Vuex -> dispatch('blogPost/read', payload)

crudl has some internal payload configs that are ommited from the final requests but can act as request settings:

preserve

It can be very useful for things like infinity scrolling.

To illustrate that:

dispatch('user/list', { page: 1 });
// ...
dispatch('user/list', { page: 2 });

Would wipe page 1 of users from your data (state), but:

dispatch('user/list', { page: 1 });
// ...
dispatch('user/list', { page: 2, crudl: { preserve: true } });

Would keep both pages on your data (state).

And could also be useful for things like refreshing the user profile without flashing an empty profile page during the request.

To illustrate that:

dispatch('user/read', { id: 1 });
// loading...
// success!
// user.name -> 'John Doe'
dispatch('user/read', { id: 1 });
// loading...
// user.name -> undefined
// success!
// user.name -> 'Johnny Doe'

Would wipe user data (state) before start the new request, but:

dispatch('user/read', { id: 1 });
// loading...
// success!
// user.name -> 'John Doe'
dispatch('user/read', { id: 1, crudl: { preserve: true } });
// loading...
// user.name -> 'John Doe'
// success!
// user.name -> 'Johnny Doe'

Would keep old user data (state) while fetching the updated one.

populate

@TODO

refresh

@TODO

Reseting data (state)

Each operation has their own respective cleaners, which can be called using:

  • Redux -> dispatch(blogPost.actions['read/clean']())
  • Vuex -> dispatch('blogPost/read/clean')

Basic customization

key

Module name

IMPORTANT: Module name should always be in the singular and in camelCase, for simplification and performance (kb-weight-wise) purposes.

new CRUDL('user');     // camelCased, singular
new CRUDL('blogPost'); // camelCased, singular

identifier

Module's primary key

By default, :id (as in blogPost.id) will be used as unique item identifiers. You can change that behavior by providing the module's primary key to crudl:

{
  identifier: 'uuid'
}

So blogPost.uuid will be used instead. Note that this will also affect routes, so /blog_posts/:id will become /blog_posts/:uuid, so remember to adapt the request payload accordingly.

client

crudl's HTTP client (axios is highly recommended)

Setting a per-module HTTP client:

{
  client: httpClient
}

You can also set a global client so you don't have to provide the client in each module initialiation:

CRUDL.client = httpClient;

include | exclude

Include or exclude module operations

{
  include: ['create', 'unknown']
} // -> [create]
{
  exclude: ['create', 'read']
} // -> [update, delete, list]
{
  include: ['create', 'read'],
  exclude: ['create']
} // -> [create]

Advanced customization

(All samples bellow will use blogPost as default module name for easier understanding). For RESTful APIs you should not need to touch these at all.

endpoints

API endpoint (path) to which the request will be fired

{
  endpoint: {
    create: '/blog_posts',
    read: '/blog_posts/:id',
    update: '/blog_posts/:id',
    delete: '/blog_posts/:id',
    list: '/blog_posts'
  }
}

keys

Key in which the expected JSON responses should be wrapped

{
  key: {
    create: 'blog_post',
    read: 'blog_post',
    update: 'blog_post',
    delete: 'blog_post',
    list: 'blog_posts'
  }
}

methods

HTTP method (verb) for each crudl action

{
  methods: {
    create: 'post',
    read: 'get',
    update: 'put',
    delete: 'delete',
    list: 'get'
  }
}

Expert customization

executors

Functions responsible for handing data (state) changes on request results

It is worth noting that for this configuration, specifically, you're required to provide all four executors (clean, start, success and failure) to crudl. You can learn more about crudl's executors by reading the default ones' source code on ./packages/core/src/executors. A few helper functions are available to assist you achie

Related Skills

View on GitHub
GitHub Stars4
CategoryDevelopment
Updated4y ago
Forks0

Languages

JavaScript

Security Score

70/100

Audited on Aug 27, 2021

No findings