Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Задачи по расписанию

Некоторые действия должны выполняться сами, без запроса от клиента: ночной отчёт, очистка устаревших данных, рассылка. Модуль @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 живёт внутри процесса и ничего не знает о других копиях. Для нескольких серверов запускайте задачи в одном отдельном процессе или используйте очередь задач.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.