Skip to content

Latest commit

 

History

History
681 lines (504 loc) · 34.5 KB

README.md

File metadata and controls

681 lines (504 loc) · 34.5 KB

Query REST

Формат параметров запроса и ответа в REST API. Для выборки связанных данных одним запросом. Спецификация является рекомендательной и не обязывает реализовывать все опции. Опции отмечены по уровню востребованности v1 v2 v3.

Содержание

Запрос

Ответ

Запрос {#request}

Спецификация определяет query параметры запроса для указания что выбрать, с каким условием, сортировкой и ограничением. Параметры опциональные, допускается их переименование и применение дополнительных. URL запроса любой.

GET /objects?fields=name,author(age)&search[author.age]=18;30&sort=-name&limi=10&skip=0&lang=ru

fields

Структура запрашиваемых данных — названия свойств и отношений объекта, которые нужно получить одним запросом.

Альтернативные названия: data, props, select, structure

В классическом REST API структура ответа жестко определяется сервером. Клиент получает лишние данные, либо сталкивается с нехваткой нужных. Например, связанные объекты нужно запрашивать отдельными запросами, имея их идентификатор. Обработка параметра fields решает эту проблему. Одним запросом можно выбрать все необходимые связанные данные и не запрашивать лишние.

Параметр fields реализуется не только в GET запросах, но и в запросах создания, изменения и удаления объектов. В каждом запросе, который способен вернуть объект.

Предполагается, что в fields указываются свойства объектов. Но если реализовать api роут для доступа к корню всех данных, то в fields будут указываться названия коллекций (типов, таблиц, множеств). В этом случаи, действительно, одним запросом можно выбрать абсолютно все необходимые данные.

GET /root?fields=users(email,phone), news(title, text), categories(name, rating)

Формат параметра search позволяет на каждое свойство определить условие выборки (фильтра).

fields = {#fields-default}

Если параметр fields не указан или равен пустой строке, то сервер возвращает минимальный набор данных исходя из своих настроек. Обычно возвращается только идентификатор объекта.

{
  "result": {
    "id": 1
  }
}

fields = * {#fields-all}

Символ * указывает на выбор всех непосредственных свойств объекта. С учётом их доступности на сервере. Выборка свойств связанных объектов не выполняется — только их внешние ключи.

{
  "result": {
    "id": 1,
    "type": "some",
    "name": "Test object",
    "status": "new",
    "profile": {
      "phone":"+79996665544",
      "avatar":{
        "id": 23,
        "type": "file"
      }
    }
  }
}

В данном примере profile является вложенным объектом (не отношением), а аватарка является отношением на объект с типом file и идентификатором 23. Для выборки свойств аватарки нужно их явно указать в fields.

fields = prop1, prop2(prop3) {#fields-map}

Конкретные свойства объекта указываются через запятую. Допустимы пробелы, переводы строк, табы. Для вложенности используются круглые скобки. Идентификатор объекта возвращается всегда (свойства по умолчанию).

GET /some/1?fields=name, profile(avatar(url, extension), prop3)
{
  "result": {
    "id": 1,
    "name": "Test object",
    "profile": {
      "avatar": {
        "url": "/uploads/1928-212/5c2f3ed1fee590496c63759f.png",
        "extension": "png"
      },
      "prop3": null
    }
  }
}

Если запрашиваемое свойство у объекта отсутствует, то в ответе оно будет с null значением. То есть в любом случаи свойство возвращается.

fields = items(prop), count {#fields-list}

Свойства списка. Если сервер возвращает список объектов, то fields применяется к каждому объекту списка. Чтобы одним запросом запросить свойства самого списка, например общее количество объектов, удовлетворяющих условию без ограничений, то запрашиваемые свойства нужно вложить в items и через запятую указывать свойства списка. Структура ответа будет консистентна запросу.

GET /some/1?fields=items(name, profile(phone)), count
{
  "result": {
    "items": [
      {
        "id": 1,
        "name": "Test object",
        "profile": {
          "phone":"+79996665544"
        }
      },
      {
        "id": 3,
        "name": "Test object 3",
        "profile": {
          "phone":"+79996665555"
        }
      }
    ],
    "count": 105
  }
}

Какие именно свойства есть у списка, зависит от реализации АПИ, по умолчанию доступен count. Можно реализовать выборку средних, минимальных значений, но чаще функции агрегации реализуются отдельными запросами.

fields = !prop {#fields-deny}

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

Сначала указывается, что нужны все свойства символом *, далее через запятую свойства, которое исключить с символом !. Если не указать *, то исключение будет из свойств, возвращаемых по умолчанию.

GET /some/1?fields=*, !name, !profile
{
  "result": {
    "id": 1,
    "type": "some",    
    "status": "new"
  }
}

fields = prop(type1:prop1, type2:prop2) {#fields-types}

Зависимость от типа объекта. В списке запрашиваемых объектов одно и то же отношение может отличаться типом связанного объекта.

Например, "объекты избранного" могут ссылаться на пользователя, статью, товар и прочие типы объектов. Соответственно, доступные свойства отношения будут различаться. Если в fields указать свойства пользователя, то для статьи они окажутся null.

Чтобы не возвращать null, нужно в fields указывать свойства для конкретного типа объекта. Перед именем свойства указывается название типа объекта, разделяя двоеточием :

GET /favorites?fields=relative(user:surname, product:price(value, unit), product:title)
{
  "result": {
    "items": [
      {
        "_id": "5c2f3ed1fee590496c93779d",
        "relative": {
          "_id": "5c2f3ed1fee590496c935678",
          "_type": "user",
          "surname": "UserSurname"
        }
      },
      {
        "_id": "5c2f3ed1fee590496c93778f",
        "relative": {
          "_id": "5c2f3ed1fee590496c935608",
          "_type": "product",
          "price": {
            "value": 100,
            "unit": "USD"
          },
          "title": "ProductTitle"
        }
      }
    ]
  }
}

Тип можно указывать для любых свойств, не только свойств отношений. Например, пользователи могут отличаться типом за счёт наследования. В одном списке будут админы, менеджеры и другие типы пользователей с различными свойствами. Уместно применять типизацию в fields для выборки исключительных свойств, например, админа или менеджера.

fields = comments(text, children(^)) {#fields-recursive}

Рекурсивные шаблоны. Символом ^ указывается ссылка на перечень полей уровнем выше скобки. Используется для выборки рекурсивных отношений (иерархий). Например, у каждого комментария есть свойство children с вложенными комментариями, у которых тоже есть вложенные комментарии.

Запрос без ^ выглядел бы так:

GET /news?fields=text, comments(text, children(text, children(text, children(text, children))

Примером выше описана выборка на три уровня вложеннности, но вложенность может быть гораздо больше. Чтобы определить поля на любой уровень вложенности применяется ссылка на шаблон полей.

GET /news?fields=text, comments(text, children(^))

Ссылка относительная. Чтобы сделать ссылку на два и более уровней выше (на шаблон за второй скобкой), указывается соответствующее количество символов ^.

GET /product?fields=title, brand(name, products(title, brand(name, products()))
GET /product?fields=title, brand(name, products(^^))

search

Условие фильтрации запрашиваемых данных.

Альтернативные названия: filter, cond, where

Параметром search указываются условия на свойства искомых объектов. Условием может быть равенство значению, вхождение в диапазон, перечень значений и другие. Допустимо указывать виртуальные свойства (поля), по которым формируются сложные условия на стороне бэкенда.

Параметром указывается множество условий на разные поля. Названия полей в квадратных скобок. Формат не определяет как логически все условия будут соединены — через И, ИЛИ, НЕ. Зависит от реализации сервера. Обычно условия соединяются через И. На усмотрение сервера конкретные условия объединять в соответствии с задачами АПИ.

Параметром search не определяются все возможные условия, в ином случаи он станет слишком сложным для использования и интерпретации сервером.

search[prop] = value {#search-eq}

Равенство значению. Строке, числу, дате, булеву. Если сервером не реализуются шаблоны условий, то в зависимости от поля и задач проекта, условие равенства может быть не строгим, например, проверятся вхождение в сроку.

search[prop] = *value {#search-text-in}

Вхождение в строку с любой позиции.

search[prop] = ^value {#search-text-start}

Вхождение в строку от начала. Если строка value короче значения свойства, то условие не выполняется.

search[prop] = ~value {#search-fulltext}

Полнотекстовый поиск. value может иметь дополнительные спец. символы, которые используются поисковым движком.

search[prop] = /value/ {#search-regex}

Регулярное выражение. Проверка строкового свойства на соответствие регулярному выражению.

search[prop] = !value {#search-not}

Неравенство значению. Для любых типов значений.

search[prop] = "value-with!~^*<>;| {#search-eq-strict}

Равенство значению со спец. символами. Если value начинается с двойной кавычки, то последующие спец. символы не интерпретируются. Выполняется условие равенства значению.

search[prop] = >value, search[prop] = <value {#search-more-less}

Больше, меньше указанного значения. Для чисел и дат.

search[prop] = >>value, search[prop] = <<value {#search-more-less-eq}

Больше или равно, меньше или равно. Так как символ равенства невозможно применить из-за особенностей URL формата, равенство задаётся двойным символом >> или <<. Для чисел и дат.

search[prop] = min;max {#search-range}

Вхождение в диапазон чисел или дат. Больше или равно min и меньше или равно max.

GET /products?search[price]=100;200  // Цена в диапазоне от 100 до 200 включительно

search[prop] = min~max {#search-interval}

Вхождение в интервал чисел или дат. Between в выборках - больше min и меньше max.

search[prop] = !min;max {#search-not-range}

Вне диапазона чисел или дат.

search[prop] = !min~max {#search-not-interval}

Вне интервала чисел или дат.

search[prop] = null {#search-null}

Отсутствие свойства или значения у свойства.

search[prop] = exp1|exp2 {#search-or}

Выполнение любого условия. Например, перечисление допустимых значений, шаблонов строк, диапазонов чисел и других условий, поддерживаемых сервером.

search[prop] = exp1&exp2 {#search-and}

Выполнение всех условий. Имеет смысл для нестрогих условий.

GET /some?search[prop] = *горо&*Москва 

search[prop] = !exp1|!exp2 {#search-or-not}

Не выполнение любого условия.

search[prop] = !exp1&!exp2 {#search-and-not}

Не выполнение всех условий.

search[prop1.prop2] = value {#search-path}

Условие по вложенному свойству (отношению). Путь на вложенное свойство через точку. Разделение точкой зарезервировано для указания пути на свойство. Формат не обязывает применять путь на свойство, если сервер может по другому именованию построить условие выборки из базы.

GET /users?search[profile.role.name]=admin // Условие явно по profile.role.name
GET /users?search[roleName]=admin // Сервер "знает", что условие по profile.role.name

search.prop1[prop] = value {#search-field}

Фильтрация множественного свойства.

Условие может быть как на выбираемое множество объектов, так и на конкретное свойство объекта (обычно тоже множественное). Например, у товара множество точек продаж, выбирая сам товар, можем ограничить список точек продаж по определенному условию.

Через точку указывается, к какому свойству применить условие фильтра. Будет отфильтровано само свойство у выбранных объектов.

GET /products?search.stores[title]=*Magnit  // Магазины отфильтровать по названию

sort

Сортировка списков.

Альтернативные названия: order

sort = prop {#sort-asc}

Сортировка по одному свойству по возрастанию. Указывается название свойства. Направление сортировки может задавать сервер, если не предоставляется выбор.

sort = -prop {#sort-desc}

Указание направления сортировки. По убыванию (desc) с символом минус - перед названием свойства без пробела. По возрастанию (asc) без символа.

sort = prop1.prop2.prop3 {#sort-path}

Сортировка по вложенному свойству (вложенного объекта или отношения). Название свойства указывается через точки в виде пути на него.

GET /stores?sort=contacts.address.street // сортировка (магазинов) по улице в адресе контактов

sort = -prop1, prop2 {#sort-many}

Сортировка по нескольким свойствам. Названия свойств перечисляются через запятую. Допустимы пробельные символы

GET /products?sort=-price,date //Сортировка по убыванию цены и возрастанию даты

sort.prop1 = prop2.prop3 {#sort-field}

Сортировка внутри множественного свойства объекта. Название сортируемого свойства указывается в названии параметра sort, а по какому полю — в значении.

GET /products?sort.stores=title // Свойство магазинов отсортировать по названию магазинов

limit

Ограничение количества возвращаемых объектов в списке.

Альтернативные названия: count, size, length

limit = {#limit-default}

Если параметр limit не передан или равен пустой строке, то сервер использует значние по умолчанию на своё усмотрение, обычно в пределах 100.

limit = 10 {#limit-number}

Ограничение количества целым числом. Сервер должен вернуть не больше указанного количества. Может вернуть меньше.

limit = * {#limit-infinity}

Без ограничений выбрать все объекты. Допускается в редких случаях, когда действительно есть необходимость гарантировано получить все объекты.

limit.prop = 10 {#limit-field}

Ограничение внутри множественного свойства объекта. Название ограничиваемого свойства указывается в названии параметра limit через точку.

GET /aritcles?limit.comments=10

skip

Позиция, с которой вернуть ограниченное количество объектов в списке. По умолчанию skip равен нулю, сервер возвращает список, начиная с первого объекта.

Альтернативные названия: offset, seek, position

skip = 10 {#skip-number}

Позиция целым числом. Если позиция больше чем общее количество объектов, то сервер вернут ноль объектов.

skip.prop = 10 {#skip-field}

Сдвиг внутри множественного свойства объекта. Название свойства указывается в параметре skip через точку.

GET /aritcles?skip.comments=10

depth

Ограничение вложенности (глубины) при выборке иерархических структур или рекурсивных связей. Обычно указывается для свойств объекта. Так как вложенная структура образуется выборкой свойств объекта.

Альтернативные названия: level

depth = {#depth-default}

Если параметр depth не передан или равен пустой строке, то сервер использует значение по умолчанию по своему усмотрению. depth используется только для рекурсивных fields.

depth.prop = 10 {#depth-field}

Ограничение максимальной вложенности рекурсивных свойств (связей) целым числом. Название свойства указывается в параметре depth через точку.

Выбор страниц 2 уровня и комментариев 4 уровня вложенности

GET /pages?depth.chilren=2&depth.comments=4

depth.prop = * {#depth-infinity}

Выбрать всю вложенность рекурсивных свойств объекта. Используется с осторожностью, если есть гарантия не зацикленности рекурсивных связей. Например, иерархию комментриев допустимо выбрать сразу всю. Но если выбирается рекурсивное свойство "друг" у пользователя, то на каком-то уровне свойство "друг" может указывать на исходного пользователя и тем самым глубина выборки окажется бесконечной.

lang

Параметр языка для мультиязычных свойств. Для выборки объектов со свойствами в нужном языке. По подобию параметра lang можно указывать валюту unit для цен, версию изменений ver и другие.

lang = {#lang-default}

Если параметр lang не передан или равен пустой строке, то для мультиязычных свойств используется язык по умолчании. Язык по умолчанию может определяться по установкам аккаунта, заголовкам HTTP или иначе.

lang = en {#lang-set}

Указание языка для всех мультиязычных свойств объекта. Код языка зависит от специфики проекта.

GET /aritcles?lang=en
{
   "title": "Title"
}

lang = * {#lang-all}

Вернуть свойства во всех языках, для которых есть перевод. Структура ответа при этом зависит от особенностей проекта. Предполагается, что мультиязычные свойства получат дополнительную вложенность в виде объекта с ключами, равным кодам языков.

GET /aritcles?lang=*
{
   "title": {
     "ru": "Заголвоок",
     "en": "Title",
     "it": "Testata"
   }
}

lang = en, ru {#lang-enum}

Выбор свойств в указанных языках. Логика аналогична выбору свойств во всех языках.

lang.prop = en {#lang-field}

Указание языка для конкретного свойства объекта. Название свойства указывается в параметре lang через точку.

GET /products?lang.stores.title=ru  // язык для заголовка магазинов

Ответ {#response}

Любой ответ от сервера возвращается в JSON. Базовая структура ответа едина для всех запросов.

Однин объект {#response-one}

Запрашиваемый объект или скалярное значение возвращается в свойстве result. Это необходимо, чтобы любой ответ сервера был валидным JSON.

{
  "result": "object or value"
}

Список объектов {#response-list}

Элементы списка возвращаются в свойстве result.items. В result могут быть дополнительные данные, например общее количество объектов для пагинации или количество страниц с учётом ограничений выборки.

{
  "result": {
    "items": ["object or value", "object or value"],
    "count": 19822
  }
}

Ошибки {#response-error}

Поддержка всех HTTP кодов форматом не определяется. Достаточно различать успешные 2xx и ошибочные запросы 4xxx в соответствии с логикой проекта.

Если сервер возвращает информацию об ошибке, то она прописывается в свойстве error. Свойство error.data содержит подробности об ошибке, например информацию про каждое ошибочное поле формы.

{
  "error": {
    "code": "400.42143",
    "message": "Some error",
    "data": {
      "fields": [
        {"path": "profile.phone", "message": "Not unique", "code": "unique"},
        {"path": "profile.phone", "message": "Bad format", "code": "format"}
      ]
    }  
  }
}

Предполагается, что у одного поля формы может быть несколько разных ошибок. В предыдущем примере все ошибки полей возвращаются массивом, а значение path элементов массива может дублироваться.