Cron-задание (Vercel)
Коротко: Маршрут, который Vercel сам автоматически вызывает в заданное время (вместо того чтобы его посещал браузер или пользователь) — для повторяющихся фоновых задач, которые никому не нужно запускать вручную.
Подробнее: В Vercel cron-задание объявляется в vercel.json (путь + расписание в формате cron, например ежедневно в определённое время). Затем Vercel сам отправляет запрос на этот маршрут — код за ним представляет собой совершенно обычный API-маршрут, технически не отличающийся от того, который вызывает человек.
Наш контекст: В Emzett /api/ticket-reminder использует это для автоматических писем-напоминаний, когда сообщения администратора в тикетах поддержки остаются без ответа 12+ часов — он запускается ежедневно, и Michael сам об этом не думает.
Подробности
Объявление и синтаксис cron
Объявление в vercel.json следует стандартному синтаксису cron (минута, час, день, месяц, день недели):
{
"crons": [
{ "path": "/api/ticket-reminder", "schedule": "0 12 * * *" },
{ "path": "/api/cart-cleanup", "schedule": "0 9 * * *" }
]
}"0 12 * * *" означает: каждый день в 12:00 UTC. Пять полей (слева направо: минута 0-59, час 0-23, день месяца 1-31, месяц 1-12, день недели 0-6) можно сочетать с * (любое значение), списками (1,15), диапазонами (1-5) и шагами (*/15 — каждые 15 минут) — тот же синтаксис, что у классического демона cron в Unix, который реализация Vercel намеренно имитирует, чтобы имеющиеся знания о cron переносились напрямую.
Аутентификация вызовов cron
Важный аспект безопасности: поскольку маршрут cron технически является совершенно обычным, публично доступным API-маршрутом, любой другой человек в принципе мог бы вызвать его вручную — поэтому Vercel устанавливает в настоящих вызовах cron особый заголовок Authorization (Bearer <CRON_SECRET>), который маршрут должен проверять на стороне сервера, чтобы третьи лица не могли вручную запускать тот же маршрут сколько угодно раз (например, чтобы многократно рассылать письма-напоминания или создавать вычислительное время/расходы):
export async function GET(request: Request) {
const authHeader = request.headers.get("authorization");
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("Unauthorized", { status: 401 });
}
// собственно логика cron
}CRON_SECRET задаётся как обычная переменная окружения и автоматически отправляется Vercel с каждым настоящим вызовом cron.
Тайм-ауты и ограничения выполнения
Маршруты cron подчиняются тем же ограничениям тайм-аута функций, что и другие serverless/edge-функции платформы — задача, длящаяся дольше заданного предела (например, отправка очень многих писем в цикле), принудительно прерывается. Для более длинных пакетных задач поэтому принято использовать маршрут cron лишь как «триггер», запускающий асинхронную фоновую задачу (например, через очередь), вместо того чтобы выполнять всю работу синхронно внутри самого запроса cron.
Ограничения по тарифу
На бесплатном тарифе Hobby частота запуска cron-заданий ограничена (например, не более одного раза в день на задание) — для более частых интервалов (каждую минуту, каждый час) нужен платный тариф. Кроме того, большинство тарифов ограничивают и максимальное число cron-заданий, которые можно одновременно настроить на проект, что может стать актуальным для проектов со многими повторяющимися фоновыми задачами.
См. также: Vercel, Переменные окружения