Как добавить блог в SaaS-приложение и не писать свою CMS

saas

Рано или поздно блог нужен любой SaaS-компании: для анонсов продукта, руководств и длинного хвоста поискового трафика, который накапливается годами. Большинство команд разработки ошибаются, когда видят в задаче «добавить блог» либо установку WordPress на выходных, либо внутренний проект по CMS на квартал. Есть третий путь, и он даёт рабочий блог на вашем домене за полдня.

Где будет жить блог: поддомен или подкаталог

Прежде чем писать код, определитесь с адресом. Вариантов три:

  • blog.yourapp.com — поддомен, который обычно указывает на отдельную стороннюю платформу.

  • yourapp.com/blog — подкаталог основного сайта, который отрисовывает ваш собственный фронтенд.

  • Чужой домен — публикация на Medium или похожей площадке.

Для SEO подкаталог сильнее всего: каждая статья наращивает авторитет домена, на котором живёт продукт, внутренние ссылки естественно связывают маркетинговые страницы и посты, а аналитика остаётся в одном ресурсе. Поддомен — рабочий вариант. На чужом домене авторитет копится целиком у кого-то другого.

У подкаталога есть архитектурное следствие: посты должно отрисовывать ваше собственное приложение. Это исключает монолитные платформы, которые непременно отдают страницы сами, и ведёт к схеме API-first: контент пишут в панели управления, получают в виде JSON и отрисовывают вашим фронтендом.

Архитектура из трёх слоёв

Блогу SaaS нужно ровно три слоя, и только один из них — ваш код:

  1. Редактирование — панель управления, где пишут посты: визуальный редактор, теги, категории, черновики, SEO-метаданные (заголовок, описание, изображение для Open Graph) и отложенная публикация.

  2. Доставка — API, который отдаёт опубликованные посты в JSON, с пагинацией, фильтром по тегам и поиском.

  3. Отрисовка — два маршрута в вашем приложении: страница списка и страница поста.

Именно на слоях 1 и 2 внутренние проекты блогов и умирают: один редактор — это недели работы, а следом идут загрузка изображений, черновики, права доступа и слаги. Контент-бэкенд даёт оба слоя как сервис, и весь ваш проект сводится к третьему.

Отрисовка: два маршрута и запрос

С типизированным SDK слой отрисовки получается небольшим. Страница списка запрашивает страницу постов:

ts
const { data: posts } = await kookee.blog.list({ page: 1, limit: 10 });

Страница поста запрашивает пост по слагу:

ts
const post = await kookee.blog.getBySlug(params.slug);

Отрисуйте текст своими компонентами в вашей дизайн-системе. В React работы ещё меньше: хуки вроде useBlogPosts и useBlogPost берут на себя состояния загрузки и пагинацию, а компонент контента сам выводит HTML поста. А можно вовсе не писать разметку: серверные компоненты отрисовывают список постов, фильтр по тегам, пагинацию и страницу поста из полученных вами данных. Подробнее — в статье Блог на React-компонентах.

Качество держат два правила:

  • Отрисовывайте на сервере. SSR или статическая генерация отдают краулерам и читателям готовый HTML. Отрисовка только на клиенте тоже работает, но тогда вы теряете SEO, ради которого выбрали подкаталог.

  • Отдавайте настоящий 404. Если слаг не найден, отвечайте статусом 404, а не оформленной страницей «не найдено» со статусом 200. Мягкие 404 засоряют индекс.

Чек-лист метаданных

Каждая страница поста должна выводить из данных, которые CMS уже хранит:

  • <title> и meta description из SEO-полей поста.

  • Канонический URL, который указывает на собственный адрес поста.

  • Теги Open Graph и Twitter Card, включая изображение поста.

  • article:published_time и article:modified_time.

  • Структурированные данные JSON-LD Article.

Всё это берётся из одного ответа API, поэтому помещается в одну вспомогательную функцию. Добавьте RSS-ленту и запись в sitemap для каждого поста: и то и другое легко сгенерировать из того же эндпоинта list, который уже вызывает страница блога.

Теги, поиск и функции, которые достаются даром

Когда посты — структурированные данные, функции, которые в самописной системе были бы отдельными проектами, становятся параметрами запроса:

  • Страницы тегов: kookee.blog.list({ tags: ['engineering'] }) на маршруте /blog/tags/:tag.

  • Поиск: kookee.blog.list({ search: query }) для строки поиска.

  • Комментарии и реакции: их отдаёт тот же API, если вы хотите, чтобы читатели откликались на посты.

  • Переводы: запрашивайте другие языковые версии поста, когда выйдете на другие рынки.

Ничто из этого не требует менять схему или писать миграции на вашей стороне: в бэкенде всё это уже есть.

Во что это обойдётся

Честный подсчёт для пути API-first:

  • Ваш код: два маршрута, функция для метаданных, лента. День работы в большинстве фреймворков, меньше, если начать с готовых шаблонов страниц.

  • Регулярно: подписка на контент-платформу. У бесплатных тарифов лимит по объёму использования (в Kookee — 100 000 запросов к API в месяц), и обычный блог долго живёт на бесплатном тарифе.

  • Что не придётся делать: писать и поддерживать редактор, хранилище медиафайлов, процесс публикации и второе развёрнутое приложение.

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

Порядок работ

  1. Создайте проект в контент-бэкенде и напишите один тестовый пост.

  2. Добавьте маршруты /blog и /blog/:slug, которые получают пост и отрисовывают его на сервере.

  3. Подключите функцию метаданных: title, description, canonical, теги OG, JSON-LD.

  4. Добавьте фильтр по тегам и RSS-ленту.

  5. Отправьте команду в панель управления и вычеркните «сделать блог» из бэклога разработки.

Сам блог сделать несложно. Сложнее направить силы разработки в продукт и при этом дать маркетингу быстрый блог в фирменном стиле на своём домене. Именно это и даёт разделение на контент-бэкенд и тонкий слой отрисовки.