RESTFUL APIs usually give you a set of endpoints to perform CRUD (Create, Read, Update, Delete) actions on different entities. We usually have a function in our project for each of these endpoints and these functions do a very similar job, but for different entities. For example, let’s say we have these functions:

// apis/users.js

// Create
export function addUser(userFormValues) {
  return fetch("/users", { method: "POST", body: userFormValues });
}

// Read
export function getListOfUsers(keyword) {
  return fetch(`/users?keyword=${keyword}`);
}

export function getUser(id) {
  return fetch(`/users/${id}`);
}


// Update
export function updateUser(id, userFormValues) {
  return fetch(`/users/${id}`, { method: "PUT", body: userFormValues });
}

// Destroy
export function removeUser(id) {
  return fetch(`/users/${id}`, { method: "DELETE" });
}

and a similar set of functions may exist for other entities like CityProductCategory, … . However, we can replace all of these functions with a simple function call:

// apis/users.js
export const users = crudBuilder("/users");

// apis/cities.js
export const cities = crudBuilder("/regions/cities");

// apis/products.js
export const cities = crudBuilder("/products");

And then use it like this:

users.add(values);
users.show(1);
users.list("john");
users.update(values);
users.remove(1);

products.update(values)


advantages

Well, there are some good reasons for writting code that way :

  • It reduces the lines of code ?-? code that you have to write and someone else has to maintain when you leave the company.
  • It enforces a naming convention for the API functions, which increases code readability and maintainability. You may have seen function names like getListOfUsersgetCitiesgetAllProductsproductIndexfetchCategories etc. that all do the same thing: “get the list of an entity”. With this approach you’ll always have entityName.list() functions and everybody on your team knows that.

So, let’s create that crudBuilder() function and then add some sugar to it.

CRUD builder

For the simple example above, the crudBuilder function would be so simple:

export function crudBuilder(baseRoute) {
  function list(keyword) {
    return fetch(`${baseRoute}?keyword=${keyword}`);
  }

  function show(id) {
    return fetch(`${baseRoute}/${id}`);
  }

  function create(formValues) {
    return fetch(baseRoute, { method: "POST", body: formValues });
  }

  function update(id, formValues) {
    return fetch(`${baseRoute}/${id}`, { method: "PUT", body: formValues });
  }

  function remove(id) {
    return fetch(`${baseRoute}/${id}`, { method: "DELETE" });
  }

  return {
    list,
    show,
    create,
    update,
    remove
  }
}

It assumes a convention for API paths and given a path prefix for an entity, it returns all the methods required to call CRUD actions on that entity.

PS: A real-world application will not be that simple! There are lots of thing to consider when applying this approach to our projects:

  • Filtering: list APIs usually get lots of filter parameters
  • Pagination: Lists are always paginated
  • Transformation: The API provided values may need some transformation before actually being used
  • Preparation: The formValues objects need some preparation before being sent to the API
  • Custom Endpoints: The endpoint for updating a specific item is not always `${baseRoute}/${id}`
Reference:

https://dev.to/s

By Shabazz

Software Engineer, MCSD, Web developer & Angular specialist

Leave a Reply

Your email address will not be published. Required fields are marked *