Почему мы отказались от REST в пользу GraphQL в Highload-проекте
Когда мы начинали разработку крупного маркетплейса, выбор архитектуры API казался очевидным — классический REST. Однако по мере роста проекта и появления новых ролей (мобильное приложение, админка, B2B-портал) мы столкнулись с классическими проблемами REST API.
Проблема: Overfetching и Underfetching
На главной странице нам нужно было показать список товаров. Мобильному клиенту требовалось только 3 поля: `id`, `title` и `price`. Но REST API `/api/products` возвращал огромный JSON на 50 полей, включая описания, характеристики и массив отзывов. Это называется Overfetching — передача избыточных данных, что забивает канал на мобильных устройствах.
В то же время, чтобы отобразить карточку товара с автором и отзывами, клиенту приходилось делать 3 разных запроса к серверу (`/products/1`, `/users/5`, `/reviews?productId=1`). Это Underfetching.
Решение: Переход на GraphQL
Мы внедрили Apollo Server (Node.js) в качестве шлюза. Теперь клиент сам решает, какие данные ему нужны.
// Запрос от мобильного клиента
query GetMobileProducts {
products {
id
title
price
}
}
Результаты перехода:
- Объем передаваемого трафика на мобильных устройствах снизился на 75%.
- Количество запросов к базе данных сократилось благодаря использованию
DataLoader (решение проблемы N+1 на уровне GraphQL).
- Frontend-разработчики перестали просить backend добавить "еще одно поле в этот эндпоинт", разработка ускорилась в 2 раза.
Как оптимизировать Next.js для Google Lighthouse на 100 баллов
В премиальном eCommerce скорость загрузки сайта напрямую влияет на конверсию. Мы поставили задачу: вывести сложный интернет-магазин с 3D-моделями и сложной фильтрацией в зеленую зону (90+ баллов) по всем показателям Core Web Vitals.
1. SSG вместо SSR где это возможно
Многие разработчики злоупотребляют getServerSideProps в Next.js. Это заставляет сервер рендерить страницу заново при каждом запросе, увеличивая показатель TTFB (Time to First Byte).
Мы перевели каталог на Static Site Generation (SSG) с использованием getStaticProps и Incremental Static Regeneration (ISR). Страницы товаров теперь лежат в кэше CDN и отдаются за 30 миллисекунд.
2. Оптимизация Largest Contentful Paint (LCP)
Главная баннерная картинка — это обычно самый большой элемент (LCP). Если загружать ее стандартным тегом <img>, Lighthouse снимет баллы.
import Image from 'next/image';
// Правильное использование next/image для LCP:
<Image
src="/hero-banner.webp"
alt="Main Banner"
priority={true} // Критично для LCP!
width={1920}
height={1080}
/>
3. Динамические импорты (Code Splitting)
Тяжелые библиотеки (например, чат поддержки или 3D-вьювер) не должны грузиться при начальной загрузке страницы. Мы использовали next/dynamic:
В итоге, показатель Total Blocking Time (TBT) снизился с 400ms до 20ms, а общий балл Performance в мобильной версии вырос с 62 до 98.
Ошибки проектирования базы данных, которые убьют ваш стартап
За годы проведения аудитов IT-инфраструктуры мы видели сотни проектов, которые начинали тормозить сразу после запуска маркетинговой кампании. И в 90% случаев проблема скрывалась не в языке программирования, а в архитектуре PostgreSQL.
Ошибка 1: Проблема N+1 в ORM
Использование ORM (Prisma, TypeORM, Sequelize) сильно упрощает жизнь разработчикам, но скрывает реальные SQL-запросы. Самая частая ошибка — запрос связанных таблиц в цикле.
Вместо того чтобы достать пользователей и их заказы одним запросом (JOIN), ORM делает 1 запрос на пользователей, а затем 100 отдельных запросов на заказы каждого пользователя. Решение: всегда использовать .include() в Prisma или JOIN FETCH, а также настраивать логирование долгих запросов на сервере.
Ошибка 2: Игнорирование индексов (и их избыток)
Отсутствие индекса на колонке, по которой часто происходит поиск (например, `user_email` или `status`), приводит к Seq Scan — полному перебору таблицы.
-- Создание правильного индекса
CREATE INDEX idx_orders_status ON orders (status) WHERE status = 'pending';
Однако есть и обратная ошибка — вешать индексы на каждую колонку. Индексы ускоряют SELECT, но замедляют INSERT и UPDATE, так как при каждой записи базе нужно перестраивать индексное дерево.
Ошибка 3: Хранение JSON там, где нужна реляция
PostgreSQL отлично работает с JSONB. Это соблазняет разработчиков складывать сложные данные (например, характеристики товара) в одну JSON-колонку. Но когда заказчику требуется отсортировать товары по значению внутри этого JSON, база начинает сильно тормозить. Если по полю нужен поиск или сортировка — это должна быть отдельная колонка в таблице.