Express / 37 минут
Query, пагинация и фильтры
Проверяйте query-параметры как недоверенные данные и стройте предсказуемую пагинацию.
- Проверяйте query-параметры как недоверенные данные и стройте предсказуемую пагинацию.
- Применить тему в задаче «Query, пагинация и фильтры» и объяснить граничные случаи.
Около 7 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Query — не готовая бизнес-модель
Query-параметры выражают дополнительные настройки запроса: поисковую строку, страницу, порядок сортировки. Они приходят из URL и являются недоверенным вводом. Конкретный parser может представлять их строками, массивами или более сложной формой, поэтому req.query.page нельзя без проверки считать числом или даже единственной строкой.
Начните с контракта: какие поля разрешены, что означает отсутствие, какие диапазоны допустимы и что делать с повторением одного параметра. Если требуются только простые строки, явно проверяйте typeof. Не вызывайте строковые методы у произвольного значения и не позволяйте настройке parser незаметно определять бизнес-правило приложения.
Значение по умолчанию и полная проверка
Отсутствующая page может означать первую страницу. Но пустая строка или 'abc' — не обязательно то же самое, что отсутствие. Подстановка через || часто объединяет разные ситуации без осознанного решения. Сначала выберите правило отсутствия, затем проверьте форму строки и только после этого преобразуйте её.
function readPage(raw) {
const value = raw ?? '1';
if (typeof value !== 'string' || !/^[0-9]+$/.test(value)) return null;
const page = Number(value);
return Number.isSafeInteger(page) && page >= 1 && page <= 100
? page : null;
}parseInt('2x',10) вернёт 2, поэтому сам по себе он не доказывает корректность всей строки. Number без предварительной проверки тоже имеет особенности, например пустая строка превращается в ноль. Явная последовательность «форма → преобразование → диапазон» делает правило читаемым и проверяемым.
Offset-пагинация
При нумерации страниц с единицы и фиксированном limit смещение вычисляется как (page - 1) * limit. Для первой страницы offset равен нулю, для второй при limit=10 — десяти. Возвращаемый список и метаданные должны использовать одну систему нумерации. Несогласованность нуля и единицы проявляется как пропущенные или повторяющиеся записи.
app.get('/items', (req, res) => {
const page = readPage(req.query.page);
if (page === null) return res.status(400).json({ error: 'page' });
const limit = 10;
res.json({ page, limit, offset: (page - 1) * limit });
});Offset прост и подходит многим небольшим спискам, но на больших изменяющихся наборах имеет ограничения стоимости и стабильности. Записи могут добавляться между запросами страниц. Поэтому важно определить порядок сортировки с устойчивым дополнительным ключом, а не рассчитывать на случайный порядок хранения.
Cursor-пагинация и стабильный порядок
Cursor описывает позицию в согласованном порядке, а не номер страницы. Его формат должен проверяться и не давать клиенту возможность управлять произвольным SQL. Если сортировка использует createdAt, часто нужен ещё уникальный id для однозначного порядка записей с одинаковым временем. Сам факт использования cursor не делает доступ к данным безопасным.
Cursor-подход удобен для ленты, но имеет другой контракт переходов и не всегда позволяет произвольно перейти к странице 57. Выбирайте модель по интерфейсу и характеру данных. Не называйте offset-параметр cursor только потому, что так моднее: клиент должен понимать реальные гарантии и ограничения API.
Фильтрация и сортировка по allowlist
Сортировочное поле, направление и разрешённые фильтры задают закрытым набором. Нельзя напрямую вставлять произвольное имя из URL в текст SQL. В библиотеке работы с БД стройте разрешённую структуру запроса через её параметризованный API. Значения и идентификаторы запросов требуют разных способов обработки.
const allowedSort = new Set(['title', 'createdAt']);
function sortField(raw) {
return typeof raw === 'string' && allowedSort.has(raw) ? raw : 'createdAt';
}Ограничивайте длину поисковой строки и максимальный размер страницы. Это одновременно помогает предсказуемости интерфейса и стоимости обработки. Если filter не соответствует допустимой форме, выберите документированный отказ либо значение по умолчанию. Не позволяйте одному endpoint молча игнорировать то, что другой считает ошибкой, без причины и описания.
Практика page от 1 до 100
В задании отсутствие параметра означает 1, а разрешённая строка содержит только цифры и даёт число в диапазоне 1..100. Нужно вернуть page, limit=10 и правильный offset. Неверные значения дают 400 с согласованным JSON. Проверка проводится через HTTP, поэтому req.query действительно проходит путь обработки Express.
Проверьте отсутствие, '2', '0', '101' и '2x'. Не используйте различие между null и строкой 'null' случайно: в solve null обозначает отсутствие query, а переданная строка является настоящим пользовательским значением. Для самостоятельного продолжения добавьте limit с отдельным верхним пределом и тест повторённого query-параметра.