Sorting REST API Results

In this article we will look at how to sort results when using a GET request that returns a list. Implementing sorting functionality in a RESTful API is a crucial aspect of designing a robust and user-friendly web service. Sorting REST API results empowers developers and end-users alike by providing a systematic and organized way to retrieve and navigate data.

In our API project we have GET /v1/travels and GET /v1/tours endpoints. Below we will describe how to implement sorting for those endpoints. If you would like a deeper dive into the process, please watch our video on this topic.

Add Sort By Query Parameter to Controllers

First, we are going update the code to handle sort_by query parameter. We update listTravels method in travels controller to include sortBy option in options passed to repository.getAll() method. sortBy is assigned the value of req.query.sort_by

const travels = TravelResource.collection(
      await repository.getAll({
        sortBy: req.query.sort_by,
      })
    );

We update listTours method in tours controller to include the same option sortBy.

const tours = TourResource.collection(
      await repository.getAll({
        sortBy: req.query.sort_by,
      })
    );

Now, let’s update BaseRepository class to handle sortBy property in the options parameter.

Handle Sorting REST API Query Parameter in Repository

Now, let’s update BaseRepository class to handle sortBy property in the options parameter. First, in BaseRepository.ts file add protected class property called allowedSortByFields

protected allowedSortByFields: Array<string> = ["created_at"];

This property is assigned a default value of an array with “created_at” as a string member. When a user attempts to sort results, the API can’t allow them to use any field they want for 2 reasons. The first one is obvious: a user may try to sort results by a field that doesn’t exist in the database. The second reason is that even if a user attempts to sort by a field that exists in the database, this field may not be indexed in the database and the operation will take a long time: for example sorting by description field.

Let’s take a look at getAll method in BaseRepository.

getAll(options: Record<string, any> = {}): Promise<Array<A>> {
    const orderBy = this.getOrderBy(options.sortBy);
    delete options.sortBy;
    options.order = orderBy;
    return this.modelClass.findAll(options);
  }

We get orderBy from options.sortBy using BaseRepository’s getOrderBy method. Then we remove sortBy from the options and add orderBy as order property on the options object. Sequelize require this property to order the returned results. Finally, we pass options to Sequelize’s findAll method.

Now let’s take a look at BaseRepository‘s getOrderBy method. It is a protected method that generates order property for Sequelize options object.

protected getOrderBy(sortBy: string | undefined): Array<[string, string]> {
    const orderBy: Array<[string, string]> = [["created_at", "DESC"]];

    if (!sortBy) {
      return orderBy;
    }

    const parts = sortBy.split("-");

    if (!this.allowedSortByFields.includes(parts[0])) {
      return orderBy;
    }

    if (!parts[1] || !["asc", "desc"].includes(parts[1].toLowerCase())) {
      return orderBy;
    }

    return [[parts[0], parts[1].toLowerCase()]];
  }

It takes sortBy parameter that can be either string or undefined and returns an array of tuples. First a default value of array with tuple created_at, desc is assigned to orderBy constant. The default value is returned in 3 cases. First – if there’s no sortBy (sortBy is undefined). Second – if the first part of the sortBy is not in the allowedSortByFields . Third – if the second part of sortBy is either missing or not “asc” or “desc”. If none of the previous conditions match, the array of tuples is returned where the first part of the tuple is the field parsed from sortBy and the second part is a direction (ascending or descending).

Update Code in Travel and Tour Repositories

Finally, let’s add protected property allowedSortByFields to TravelRepository.

protected allowedSortByFields = [
    "name",
    "number_of_days",
    "created_at",
    "updated_at",
  ];

As you can see, we can sort Travels by name, number of days, created_at and updated_at.

Let’s add the same protected property to TourRepository.

protected allowedSortByFields = [
    "name",
    "price",
    "starting_date",
    "ending_date",
  ];

We can sort tours by name, price, starting_date and ending_date.

That’s it for the code. Inheritance pattern enables us to “push” all the logic into parent class. Children classes only need to have their own custom properties or logic.

Conclusion

In conclusion, incorporating sorting functionality into your RESTful API design is not just a feature — it’s a strategic decision that significantly impacts the usability and efficiency of your web service. By providing users and developers with the tools to organize and navigate data effectively, you create a more powerful and user-centric API experience.

Share this article

Posted

in

by