Пагинация для RESTful API на Swagger Node

В предыдущей статье мы написали на Swagger Node небольшой простенький API для работы с коллекцией книг. Однако там есть нюанс. Предположим, у нас собралась большая библиотека из нескольких десятков тысяч книг. Каждый раз, когда мы делаем запрос GET /books, наш API отдает полную коллекцию. Это отражается на скорости работы нашего приложения и количестве переданного трафика. Поэтому в этой статье попробуем организовать частичную выдачу книг.

Редактируем API

Для организации постраничной выдачи всех наших книг нам нужно 3 параметра:

  • count - общее количество книг в коллекции;
  • limit - количество книг в одном ответе (показываемых на одной странице);
  • skip - количество пропущенных книг от начала и до первой из текущей выдачи (количество книг на предыдущих страницах).

Зная эти параметры в клиентском приложении легко можно рассчитать общее количество страниц, номер текущей страницы, номера последних страниц.

skip и limit мы будем передавать в качестве параметров запроса. count должен вернуть в ответе сервер.

В файле swagger.yaml в самом конце добавим описания параметров:

parameters:
  skipParam:
    name: skip
    in: query
    description: number of items to skip
    required: false
    type: integer
    format: int32
    minimum: 0
  limitParam:
    name: limit
    in: query
    description: max records to return
    required: false
    type: integer
    format: int32
    maximum: 100
    minimum: 0

Ограничим минимальное значение для skip и limit и максимальное значение для limit. Передавать их будем в query запроса.

Добавим описание схемы для ответа с постраничной выдачей книг в definitions::

Books:
  properties:
    paging:
      type: object
      properties:
        skip:
          type: integer
        limit: 
          type: integer
        count: 
          type: integer
    data:
      type: array
      items:
        $ref: '#/definitions/Book'
  required:
      - paging
      - data

Как видим, теперь ответ сервера должен содержать объект paging с параметрами пагинации и массив с книгами data.

Подправим маршрут GET /books следующим образом:

get:
  description: Return a books list
  parameters:
    - $ref: "#/parameters/skipParam"
    - $ref: "#/parameters/limitParam"
  responses:
    "200":
      description: Success 
      schema:
        $ref: '#/definitions/Books'
    # responses may fall through to errors
    default:
      description: Error
      schema:
        $ref: "#/definitions/ErrorResponse"

Перезапустим сервер в mock-режиме:

swagger project start -m

и попробуем отправить запрос.

Работает. Можно браться за контроллер.

Модифицируем контроллер

Заменим код функции getAll на следующий:

function getAll(req, res) {
    var skip = req.swagger.params.skip.value ? req.swagger.params.skip.value : 0;
    var limit = req.swagger.params.limit.value ? req.swagger.params.limit.value : 10;

    Book.find().count(function (err, count) {

        Book.find().skip(skip).limit(limit).exec(function (err, books) {
            if (err) {
                throw err;
            } else {
                res.json({
                        paging: {
                            count: count,
                            skip: skip,
                            limit: limit
                        },
                        data: books
                    }
                );
            }
        });
    });
}

Сперва считываем параметры skip и limit и присваиваем значения по умолчанию, если они не заданы. Затем выполняем запрос в БД для определения общего количества книг count. А потом с заданными параметрами делаем соответствующую выборку книг из БД и отдаем ответ клиенту.

Убедимся, что в swagger.yaml для GET /books задан operationId: getAll. Создадим несколько книг методом POST /books и попробуем поиграться с параметрами пагинации.

Посмотреть код целиком можно в
этой ветке на гитхабе.

Можете критиковать.