Универсальность данных: 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 становится деталью реализации, от которой вы абстрагируетесь.