Шаблон RESTful великолепен, он упрощает наши API-интерфейсы и веб-сайты и удерживает разработчиков от попыток выставить напоказ свои URL-адреса. Однако программистам часто бывает сложно понять, как применить это к интерфейсу. Это кажется слишком большим, но, разбив его на части, мы увидим, что это лучший способ получить данные из базы данных.

REST для фронтенда даже проще, чем для бэкенда

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

Настройка и обзор

Мы сделаем 5 методов: getAll(), getOne(), create(), update() и, конечно же, всеми любимый destroy(). Я предполагаю, что у вас есть базовый сервер, с которым можно возиться, и приличное понимание выборки. Если нет, то вот моя статья об использовании JSON Server для быстрой настройки поддельного локального RESTful-сервера и еще одна о работе с fetch(). Или просто воспользуйтесь гуглом, это свободная страна, поживите немного.

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

Один способ получить их все

getAll() - простейший из наших методов, но очень важный. Его задача - показать все, что есть в вашей базе данных. Вот:

function getAll() {
  return fetch("http://localhost:3000/tasks")
    .then((response) => response.json())
}

Мы возвращаем все, что получаем, поскольку действительно хотим работать с этим значением вне функции. Затем мы извлекаем маршрут индекса REST из нашей базы данных, который вернет обработанное обещание со значением, являющимся объектом ответа. Чтобы получить доступ к нашим данным, мы возвращаем объект JSON, используя метод ответа json(). Помните, что мы возвращаем окончательное значение, стрелочные функции имеют неявный возврат, если они составляют всего одну строку.

Это более или менее шаблон для всех методов. Мы извлечем, получим объект ответа, а затем вернем обработанное обещание с нашим объектом JSON в качестве значения. Позже наш код сможет работать с этим, просто подключив еще один then(), например: getAll().then( /* work with our JSON object*/)

Теперь получите только один

Хорошо, это ВСЕ наши задачи, но что, если нам нужна только одна? Это в основном то же самое:

function getOne(id) {
  return fetch("http://localhost:3000/tasks/" + id)
    .then((response) => response.json())
}

Единственное фактическое изменение - это URL-адрес, идентификатор, который мы передаем, направит нас на маршрут этого ресурса. Но подождите, этот код не сухой; наш базовый URL http: // localhost: 3000 / tasks был таким же, как и предыдущий, почему бы нам не поместить его в переменную?

const baseUrl = "http://localhost:3000/tasks"
function getAll() {
  return fetch(baseUrl)
    .then((response) => response.json())
}
function getOne(id) {
  return fetch(`${baseUrl}/${id}`)
    .then((response) => response.json())
}

Отлично.

Далее: уничтожить

Двигаясь вверх по цепочке, мы приходим к destroy(). Теперь наша выборка не просто извлекает данные из нашей базы данных, она фактически удаляет их. Чтобы это сработало, мы должны отправить некоторые настройки вместе с нашим URL-адресом в нашем fetch():

function destroy(id) {
  let options = { method: 'DELETE' }
  return fetch(`${baseUrl}/${id}`, options)
    .then((response) => response.json())
}

Этот объект параметров заполняет init необязательный параметр, но его вызов options делает немного понятнее, что мы делаем. У нас есть несколько вариантов настроек получения, и мы решаем, какие они. Когда дело доходит до destroy(), мы должны указать fetch() не использовать HTTP-метод GET по умолчанию, а вместо этого использовать DELETE, чтобы наш сервер фактически удалил ресурс по этому URL-адресу.

Почти готово: давайте создадим его

Еще раз поднимаясь на уровень сложности, для create() мы должны теперь отправить заголовки и тело в наших параметрах:

function create(data) {
  let options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  }
  return fetch(baseUrl, options)
    .then((response) => response.json())
}

Причина отправки тела проста: это фактические данные, которые мы хотим сохранить в базе данных. Однако, прежде чем мы сможем его отправить, мы должны его структурировать. Помните, что мы можем отправлять и получать только строки, наши программы на обоих концах должны анализировать данные.

И чтобы помочь серверу разобрать его, мы прикрепляем вложенный объект headers. Сервер просто получит строку, ему нужны заголовки в запросе, чтобы сообщить ему, на какие данные он смотрит. В девяти случаях из десяти это будет просто JSON, поэтому мы используем ‘Content-Type’: ‘application/json’.

Вам также может быть интересно, почему мы все еще что-то возвращаем, если все, что мы делаем, это отправляем что-то в базу данных. Хотя в консоли полезно видеть, какой объект был создан в базе данных, в основном это (в конечном итоге) для рендеринга нашей страницы. Возвращая вновь созданный объект, мы могли бы добавить этот единственный элемент на нашу страницу, вместо того, чтобы пытаться получить весь новый список задач, чтобы получить новый в конце.

Последнее обновление: обновление

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

function update(id,data) {
  let options = {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  };
  return fetch(`${baseUrl}/${id}`, options)
    .then((response) => response.json())
}

Небольшое предупреждение: в то время как другие наши http-глаголы не обязательно должны быть заглавными, во избежание проблем с CORS вы всегда должны указывать PATCH в верхнем регистре. update() в основном то же самое, что и create(), мы просто получаем URL-адрес определенного ресурса вместо baseUrl. Опять же, мы возвращаем наш недавно отредактированный объект, поэтому в конечном итоге сможем правильно его отрендерить.

Импровизировать. Адаптировать. Принести.

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

Кроме того, в более крупных проектах может быть несколько адаптеров, поэтому рекомендуется как-то инкапсулировать эти функции, а затем помещать их в отдельный файл. Файл называется адаптером.js или конкретным адаптером, например, taskadapter.js. Для инкапсуляции я лично предпочитаю помещать их все в объект, чтобы было ясно, к какому объекту принадлежат эти методы, когда я использую их в своем общем коде. Наш последний адаптер будет выглядеть примерно так:

(Вы заметите, что я также создал вспомогательную функцию toJSON(), поэтому мне не нужно было повторяться, но она делает то же самое)

И вот оно! Базовый шаблон, который вам понадобится для работы с базой данных RESTful. Если что-то кажется вам непонятным, просто продолжайте экспериментировать с этим в консоли, и я знаю, что вы это поймете.

Всем удачного кодирования,

Майк