Scheduled tasks
Some actions must run on their own, without a client request: a nightly report, cleaning up stale data, a mailing. The @nestjs/schedule module lets you mark a service method with a decorator, and NestJS calls it on schedule while the server is running.
Installation and setup
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() is added once, in the root module. It then finds marked methods in every service.
A scheduled task
Let the blog log every night how many posts appeared during the day. Create a service:
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(`New posts in the last day: ${count}`);
}
}Every day at midnight the server terminal shows a line like [PostsReportService] New posts in the last day: 3. MoreThan() from TypeORM sets a "greater than" condition. The Logger class is covered in the next lesson.
Cron syntax
Instead of the ready constants you can write a cron string — five or six space-separated fields:
* * * * * *
│ │ │ │ │ └─ day of week (0–7)
│ │ │ │ └─── month (1–12)
│ │ │ └───── day of month (1–31)
│ │ └─────── hour (0–23)
│ └───────── minute (0–59)
└─────────── second (0–59, optional)| Notation | When it fires |
|---|---|
@Cron('0 */10 * * * *') | Every 10 minutes |
@Cron('0 0 9 * * 1') | Mondays at 9:00 |
@Cron(CronExpression.EVERY_HOUR) | At the start of every hour |
To run a task in a particular time zone, add a second argument: @Cron('0 0 9 * * 1', { timeZone: 'Europe/Berlin' }).
Intervals and one-off runs
// src/posts/posts-report.service.ts
import { Interval, Timeout } from '@nestjs/schedule';
@Interval(60_000)
checkQueue() {
this.logger.log('Checking the queue');
}
@Timeout(5_000)
warmUp() {
this.logger.log('The server started five seconds ago');
}@Interval() calls the method every 60 seconds, counting from server start. @Timeout() calls the method once, five seconds after start.
Common mistake
If you run several copies of the server (for example, to handle load), the task runs in each copy — the report arrives three times and emails go out three times. The schedule in @nestjs/schedule lives inside the process and knows nothing about other copies. With several servers, run tasks in one separate process or use a job queue.