Пиши один раз, публикуй везде автоматически: Notion + Next.js + Make.com
Самое сложное в ведении блога — не писать, а распространять. Я убедился в этом на собственном опыте: статья готова, но её нужно выложить на сайт, потом анонсировать в Telegram-канале, потом написать пост в LinkedIn. Каждый шаг — ручная работа, и на каждом легко что-то забыть. В итоге для madrimov.uz я построил пайплайн, в котором я только пишу в Notion — сайт, Telegram и LinkedIn автоматика берёт на себя. Ниже — архитектура этой реальной системы, сценарии Make.com и уроки, полученные по пути.
Проблема: одна статья — три отдельные задачи
Раньше процесс выглядел так: пишу статью, вручную выкладываю на сайт, потом, когда вспомню, кидаю ссылку в Telegram-канал, а LinkedIn чаще всего вообще забывается. Результат:
- Много ручной работы: три-четыре copy-paste на одну статью.
- Теряется последовательность: в канал — сегодня, на сайт — завтра, или наоборот.
- Ломается ритм: в загруженную неделю не выходит вообще ничего.
Самый дорогой ресурс в производстве контента — внимание, которое уходит на письмо. Дистрибуция же — механическая работа, а значит, её должна делать машина.
Архитектура: Notion — единственный источник контента
Вся система строится на одном принципе: контент живёт только в одном месте. У меня это Notion database. Каждая статья — одна запись с нужными property:
- Title и Slug — для URL на сайте.
- Контент на трёх языках — узбекский, русский, английский (сайт трёхъязычный).
- Tags — multi-select, по темам.
- Published — checkbox: именно он решает, видна ли статья на сайте.
- PublishDate — запланированная дата выхода.
- TgPosted и LiPosted — checkbox'ы: ушла ли статья в канал.
Notion здесь играет роль CMS: удобный редактор, мобильное приложение, история версий — всё бесплатно. Контент я больше никуда не копирую: и сайт, и сценарии читают ровно из этой базы.
На стороне Next.js: ISR и fetch без зависимостей
Сайт на Next.js. Данные из Notion я получаю без официального SDK, обычным fetch — API сводится к одному POST-запросу, и держать ради этого отдельную зависимость незачем:
// lib/notion.ts
export async function getPublishedPosts() {
const res = await fetch(
`https://api.notion.com/v1/databases/${process.env.NOTION_DB_ID}/query`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NOTION_TOKEN}`,
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
},
body: JSON.stringify({
filter: { property: "Published", checkbox: { equals: true } },
sorts: [{ property: "PublishDate", direction: "descending" }],
}),
}
);
if (!res.ok) throw new Error(`Notion API: ${res.status}`);
const data = await res.json();
return data.results;
}Для обновления достаточно ISR (Incremental Static Regeneration):
// app/blog/page.tsx
export const revalidate = 300; // 5 минутОтметили Published в Notion — максимум через 5 минут статья на сайте. Никаких webhook'ов, build-триггеров и ручных деплоев. Для блога задержка в 5 минут никому не мешает, зато система остаётся предельно простой.
Make.com: автоматический тизер в Telegram-канал
Теперь дистрибуция. Для канала @madrimovblog в Make.com есть сценарий из трёх модулей, который запускается каждое утро в 9:00:
- Notion — Query a Database: находит записи с
Published = trueиTgPosted = false, лимит — 1. - Telegram — Send a Message: отправляет в канал пост с тизером и ссылкой
https://madrimov.uz/blog/{slug}. - Notion — Update a Database Item: ставит у этой записи
TgPosted = true.
Логика фильтра на языке Notion API выглядит так:
{
"and": [
{ "property": "Published", "checkbox": { "equals": true } },
{ "property": "TgPosted", "checkbox": { "equals": false } }
]
}Третий шаг — сердце дедупликации: если не отметить, завтра сценарий найдёт ту же статью снова, и канал превратится в спам. Если подходящих записей нет, сценарий просто тихо завершается — это нормальное состояние, а не ошибка.
LinkedIn: тот же паттерн, другой checkbox
Для LinkedIn — отдельный сценарий, но паттерн ровно тот же: Published = true & LiPosted = false → отправить пост → LiPosted = true. Отличается только формат текста — в LinkedIn уходит английская аннотация и чуть более официальный тон. Добавить новый канал дистрибуции теперь очень дёшево: один checkbox property и одна копия сценария.
Очередь: ритм «одна статья в неделю» держит автоматика
Моя любимая часть системы. Когда приходит вдохновение, я пишу подряд две-три статьи, каждой ставлю будущую PublishDate, а Published оставляю выключенным. Еженедельный сценарий (в понедельник утром) делает следующее:
- Находит запись с
Published = falseиPublishDate <= сегодня. - Ставит у неё
Published = true.
Одно изменение checkbox'а запускает всю цепочку: через 5 минут ISR выводит статью на сайт, на следующее утро сценарий публикует её в Telegram, затем в LinkedIn. Даже если я в отпуске, блог живёт в ритме «одна статья в неделю». Это и есть очередь контента: письмо пусть зависит от вдохновения, а выход — от расписания.
Почему Make.com, а не собственный код?
Я разработчик и мог бы написать эти сценарии сам — cron плюс небольшой скрипт. Но сознательно не стал:
- Для cron нужно держать сервер (или serverless-функцию) и мониторить его — это тоже инфраструктура, пусть и маленькая.
- В Make retry, лог ошибок и история запусков есть из коробки. Если Telegram API однажды вернёт 500, он сам повторит запрос.
- Сценарий видно визуально: открыв его через полгода, я понимаю всё с одного взгляда.
Когда лучше писать свой код? При сложных трансформациях (например, рендеринг markdown в другой формат), на больших объёмах (Make берёт деньги за количество операций — на тысячах запусков это дорого) и при работе с чувствительными данными. В моём случае около 30 операций в месяц — умещается даже в бесплатный тариф.
Уроки
Идемпотентность — правило номер один. Для каждого канала дистрибуции — отдельный checkbox «опубликовано». Без него при повторном запуске сценария (retry, ручной тест) подписчики получат один пост дважды. Благодаря фильтру по checkbox'у вторая попытка просто вернёт пустой результат и ничего не отправит.
Помечайте тестовые записи отдельно. Однажды моя тестовая статья «asdf» едва не улетела в канал. Теперь у тестовых записей специальный тег, и все фильтры их исключают. Автоматика не отличает тест от настоящего контента — это должны сделать вы.
Имена property — это API-контракт. Переименовать property в Notion легко — один клик. Но если поменять TgPosted на TelegramPosted, молча сломаются и сценарий Make, и код сайта: Notion не выдаст ошибку, просто вернёт пустое значение. Соберите имена в один файл констант на стороне кода и дважды подумайте, прежде чем переименовывать что-то в Notion.
Вывод
Вся система: Notion database, 30 строк fetch-кода в Next.js и три маленьких сценария в Make. Никакого отдельного сервера, никакой сложной цепочки деплоев. А главный результат не технический: когда исчезло трение между письмом и публикацией, самого письма стало больше. Если вы тоже ведёте блог, канал или любой другой поток контента — напишите один раз, а остальное отдайте машине.