DEV Community

Cover image for Multiple fields sorting with a time saving function for server side developer (api pagination)
Nurul Islam Rimon
Nurul Islam Rimon

Posted on

Multiple fields sorting with a time saving function for server side developer (api pagination)

Use case:
Sort by asc or desc
https://your-url?sort[first_name]=desc&sort[last_name]=asc

or, Sort by ascending / descending
https://your-url?sort[first_name]=ascending&sort[last_name]=descending

or, Sort by 1 / -1
https://your-url?sort[first_name]=1&sort[last_name]=-1

This function will help you to sort by using sortBy and sortOrder fields also.
https://your-url?sortOrder=desc&sortBy=last_name

Code for Typescript:

type ISortOrder = "asc" | "desc" | "ascending" | "descending" | 1 | -1;

export interface IPaginationFields {
    page?: number;
    limit?: number;
    sortBy?: string;
    sortOrder?: ISortOrder;
    sort?: Record<string, ISortOrder>;
}

export interface IFormatedPagination {
    skip: number;
    page: number;
    limit: number;
    sort: { [key: string]: 1 | -1 };
}

export const formatPagination = (
    pagination: IPaginationFields
): IFormatedPagination => {
    const { limit = 10, page = 1, sortBy, sortOrder, sort } = pagination;

    const formattedSort: { [key: string]: 1 | -1 } = {};

    const formatOrder = (value: string | number) => {
        const numValue = Number(value);
        const isString = Number.isNaN(numValue);

        if (!isString && (numValue === 1 || numValue === -1)) {
            return numValue;
        } else {
            return value === "asc" || value === "ascending" ? 1 : -1;
        }
    };

    if (sortBy) {
        const sortByArr = Array.isArray(sortBy) ? sortBy : [sortBy];
        const sortOrderArr = Array.isArray(sortOrder) ? sortOrder : [sortOrder];
        sortByArr.forEach((field, index) => {
            formattedSort[field] = formatOrder(sortOrderArr[index]);
        });
    }

    if (!Array.isArray(sort) && typeof sort === "object") {
        Object.keys(sort).forEach((field) => {
            formattedSort[field] = formatOrder(sort[field]);
        });
    }

    if (!formattedSort.createdAt) {
        formattedSort.createdAt = -1;
    }

    return {
        skip: (page - 1) * limit,
        limit: Number(limit),
        page: Number(page),
        sort: formattedSort,
    };
};

Enter fullscreen mode Exit fullscreen mode

Code for javascript:

const formatPagination = (pagination) => {
    const { limit = 10, page = 1, sortBy, sortOrder, sort } = pagination;

    const formattedSort = {};

    const formatOrder = (value) => {
      const numValue = Number(value);
      const isString = Number.isNaN(numValue);

      if (!isString && (numValue === 1 || numValue === -1)) {
        return numValue;
      } else {
        return value === "asc" || value === "ascending" ? 1 : -1;
      }
    };

    if (sortBy) {
      const sortByArr = Array.isArray(sortBy) ? sortBy : [sortBy];
      const sortOrderArr = Array.isArray(sortOrder) ? sortOrder : [sortOrder];
      sortByArr.forEach((field, index) => {
        formattedSort[field] = formatOrder(sortOrderArr[index]);
      });
    }

    if (!Array.isArray(sort) && typeof sort === "object") {
      Object.keys(sort).forEach((field) => {
        formattedSort[field] = formatOrder(sort[field]);
      });
    }

    if (!formattedSort.createdAt) {
      formattedSort.createdAt = -1;
    }

    return {
      skip: (page - 1) * limit,
      limit: Number(limit),
      page: Number(page),
      sort: formattedSort,
    };
  };

  return formatPagination;
Enter fullscreen mode Exit fullscreen mode

Overview
The code defines an interface for pagination and sorting fields, as well as a utility function to format these fields into a structure suitable for database queries or other pagination use cases. This utility helps in standardizing the pagination and sorting process.

Code Explanation
Interfaces
ISortOrder

  • Represents the possible values for sorting order:
  • Strings: "asc", "desc", "ascending", "descending" - Numbers: 1 (ascending), -1 (descending)

IPaginationFields

  • Describes the input structure for pagination and sorting:

  • page (optional): The current page number (default: 1).

  • limit (optional): The number of items per page (default: 10).

  • sortBy (optional): A field name (or an array of field names) to sort by.

  • sortOrder (optional): The sorting order(s) corresponding to sortBy.

  • sort (optional): An object where keys are field names and values are sort orders.

IFormatedPagination

  • Describes the output structure for formatted pagination:

  • skip: The number of items to skip (calculated as (page - 1) * limit).

  • page: The current page number.

  • limit: The number of items per page.

  • sort: A key-value pair of field names and their corresponding sort orders (1 or -1).


formatPagination Function
This function processes the input pagination object and converts it into a standardized format.

Parameters

  • pagination: An object implementing the IPaginationFields interface.

Steps
Defaults

  • Destructures the pagination input and assigns default values to limit (10) and page (1).

  • Initializes an empty object formattedSort for storing the formatted sort fields.

Helper Function: formatOrder

  • Converts the given sorting order (value) into a numeric format (1 or -1):

  • If value is a number (1 or -1), it returns the number.

  • If value is a string (asc or ascending), it returns 1.

  • For desc or descending, it returns -1.

Process sortBy and sortOrder

  • Handles the case where sortBy and sortOrder are arrays or single values:

  • Converts them to arrays (if not already).

  • Iterates over the fields in sortBy and assigns their corresponding orders

  • from sortOrder (using formatOrder) to formattedSort.

Process sort Object

  • If sort is an object (and not an array), iterates over its keys:

  • Converts each key's value into a numeric order using formatOrder and adds it to formattedSort.

Default Sort Field

  • If formattedSort does not include the createdAt field, adds it with a descending order (-1).

Return the Result

  • Returns an object with the following properties:

  • skip: Number of items to skip, calculated as (page - 1) * limit.

  • limit: The number of items per page.

  • page: The current page number.

  • sort: The processed sorting object.

const pagination: IPaginationFields = {
  page: 2,
  limit: 20,
  sortBy: ['name', 'date'],
  sortOrder: ['asc', 'desc'],
  sort: { age: 1, score: -1 },
};

const formatted = formatPagination(pagination);

console.log(formatted);
/*
{
  skip: 20,
  limit: 20,
  page: 2,
  sort: {
    name: 1,
    date: -1,
    age: 1,
    score: -1,
    createdAt: -1, // Default sort field
  },
}
*/
Enter fullscreen mode Exit fullscreen mode

Key Features

  • Flexible Input: Handles single and array values for sortBy and sortOrder.
  • Default Values: Ensures pagination works even with incomplete input.
  • Default Sorting: Adds a fallback sort field (createdAt).
  • Custom Sorting: Supports a sort object for complex sorting scenarios. This utility is ideal for applications that require standardized pagination and sorting, such as REST APIs or database queries

Top comments (0)