Задачи по расписанию
Некоторые действия должны выполняться сами, без запроса от клиента: ночной отчёт, очистка устаревших данных, рассылка. Модуль @nestjs/schedule позволяет пометить метод сервиса декоратором, и NestJS будет вызывать его по расписанию, пока работает сервер.
Установка и подключение
npm i @nestjs/schedule// src/app.module.ts
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [ScheduleModule.forRoot(), PostsModule],
})
export class AppModule {}ScheduleModule.forRoot() подключают один раз в корневом модуле. После этого он находит помеченные методы во всех сервисах.
Задача по расписанию
Пусть блог каждую ночь пишет в журнал, сколько статей появилось за сутки. Создайте сервис:
nest g service posts/posts-report --flat// src/posts/posts-report.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { MoreThan, Repository } from 'typeorm';
import { Post } from './entities/post.entity';
@Injectable()
export class PostsReportService {
private readonly logger = new Logger(PostsReportService.name);
constructor(
@InjectRepository(Post)
private readonly postsRepository: Repository<Post>,
) {}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async dailyReport() {
const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
const count = await this.postsRepository.count({ where: { createdAt: MoreThan(since) } });
this.logger.log(`Новых статей за сутки: ${count}`);
}
}Каждый день в полночь в терминале сервера появится строка вида [PostsReportService] Новых статей за сутки: 3. MoreThan() из TypeORM задаёт условие «больше чем». Класс Logger подробно разобран в следующем уроке.
Расписание в формате cron
Вместо готовых констант можно написать строку cron — пять или шесть полей через пробел:
* * * * * *
│ │ │ │ │ └─ день недели (0–7)
│ │ │ │ └─── месяц (1–12)
│ │ │ └───── день месяца (1–31)
│ │ └─────── час (0–23)
│ └───────── минута (0–59)
└─────────── секунда (0–59, необязательно)| Запись | Когда срабатывает |
|---|---|
@Cron('0 */10 * * * *') | Каждые 10 минут |
@Cron('0 0 9 * * 1') | По понедельникам в 9:00 |
@Cron(CronExpression.EVERY_HOUR) | В начале каждого часа |
Чтобы задача шла по нужному часовому поясу, добавьте второй аргумент: @Cron('0 0 9 * * 1', { timeZone: 'Europe/Berlin' }).
Интервал и разовый запуск
// src/posts/posts-report.service.ts
import { Interval, Timeout } from '@nestjs/schedule';
@Interval(60_000)
checkQueue() {
this.logger.log('Проверка очереди');
}
@Timeout(5_000)
warmUp() {
this.logger.log('Сервер запущен пять секунд назад');
}@Interval() вызывает метод каждые 60 секунд, отсчёт идёт от запуска сервера. @Timeout() вызывает метод один раз через пять секунд после запуска.
Частая ошибка
Если запустить несколько копий сервера (например, для нагрузки), задача выполнится в каждой копии — отчёт придёт трижды, а письма уйдут по три раза. Расписание в @nestjs/schedule живёт внутри процесса и ничего не знает о других копиях. Для нескольких серверов запускайте задачи в одном отдельном процессе или используйте очередь задач.