Универсальность данных: API геттер и сеттер
Вместо этого предлагаю создавать функции, единственной целью которых является получение и передача данных API. Это позволит изменять конечную точку API и даже то, как API возвращает данные, без необходимости переписывать значительную часть кодовой базы.
Пример
Представим, что есть приложение todo.
Пользователи могут просматривать список дел, добавлять новые и отмечать их как завершённые. Каждое из перечисленных действий вызывает API, который получает, устанавливает или изменяет данные.
Допустим, конечная точка API — /todos.
Запрос GET возвращает все данные, запрос POST создаёт новый элемент todo, а запрос PUT обновляет существующий элемент todo.
// Данные полученные из GET
let data = [{
id: 1234
todo: 'Play D&D',
completed: false
},
{
id: 5678
todo: 'Buy milk',
complete: true
}];
// Формат данных для методов POST и PUT
let update = {
id: 1234
todo: 'Play D&D',
completed: true
};Прямое использование конечных точек
Иногда я вижу, что конечные точки API вызываются непосредственно в коде, использующем их данные.
async function renderTodos () {
// Получение данных API
let request = await fetch('/todos');
let todos = await request.json();
// Рендер данных
let app = document.querySelector('#app');
app.innerHTML =
`<ul>
${todos.map((todo) => {
return `<li>${todo}</li>`;
}).join('')}
</ul>`;
}Всё работает отлично… пока не перестаёт.
Изменения в API
Вполне вероятно, что приложение обращается к API в разных местах для решения различных задач. Когда API изменяется, это означает переписывание большого количества кода.
Возможно, меняется конечная точка с /todos на /api/v2/todos. Достаточно простое изменение, но потенциально разбросанное по разным файлам.
Возможно, изменился способ обработки аутентификации. Теперь необходимо обновить запрос fetch() везде, где он используется.
Иногда изменяются возвращаемые данные.
// Исходный GET ответ
let data = [{
id: 1234
todo: 'Play D&D',
completed: false
},
{
id: 5678
todo: 'Buy milk',
complete: true
}];
// Новый GET ответ
let newData = {
1234: {
todo: 'Play D&D',
completed: false
},
5678: {
todo: 'Buy milk',
complete: true
}
};Теперь, везде, где вызывается API, нужно не просто обновить вызов API, но и рефакторить то, как вы с ним работаете.
Но есть и более простой способ.
Методы геттер и сеттер
Я рекомендую клиентам использовать специальные методы для вызова API и обработки полученных данных.
// Получение данных API
async function getTodos () {
let request = await fetch('/todos');
let todos = await request.json();
return todos;
}
// Рендер данных todo
async function renderTodos () {
let todos = await getTodos();
let app = document.querySelector('#app');
app.innerHTML =
`<ul>
${todos.map((todo) => {
return `<li>${todo}</li>`;
}).join('')}
</ul>`;
}Если API изменится, у вас будет один файл, в который нужно внести изменения.
А при более серьёзных изменениях, например, при значительном изменении способа возврата данных, можно преобразовать их перед возвратом и вообще не обновлять использующие их функции.
// Получение данных API
async function getTodos () {
// Получение данных API
let request = await fetch('/todos');
let todos = await request.json();
// Преобразование данных в старый формат
let transformed = Object.entries(todos).map(([id, item]) => {
let {todo, completed} = item;
return {id, todo, completed};
});
// Возвращение преобразованных данных
return transformed;
}При таком подходе renderTodos() и любые другие функции, использующие вывод getTodos(), не нуждаются в изменениях.
Специфичность API становится деталью реализации, от которой вы абстрагируетесь.