NestJS Cron Jobs Tutorial for Task Scheduling
Some tasks need to run automatically, on a schedule, without any user triggering them: cleaning up expired data, sending a daily summary email, or refreshing a cache every hour. NestJS's official @nestjs/schedule package brings this kind of task scheduling directly into the framework's own dependency-injection-aware architecture.
NestJS CRON Jobs Tutorial: Learning Objectives
- Install and configure the official @nestjs/schedule package.
- Schedule a recurring task using the @Cron() decorator and cron expression syntax.
- Schedule tasks using simpler @Interval() and @Timeout() decorators.
- Understand basic cron expression syntax for common scheduling patterns.
- Recognize when scheduled tasks need special handling in multi-instance deployments.
NestJS CRON Jobs Tutorial: Key Terms and Definitions
- @nestjs/schedule: The official NestJS package providing decorators for scheduling recurring or delayed tasks.
- Cron expression: A string-based syntax (five or six space-separated fields) describing a recurring schedule, such as 'run every day at midnight'.
- @Cron() decorator: Marks a method to run automatically according to a specified cron expression.
- @Interval() decorator: Marks a method to run repeatedly at a fixed time interval, specified in milliseconds.
- @Timeout() decorator: Marks a method to run exactly once, after a specified delay following application startup.
How NestJS CRON Jobs Tutorial Works: Detailed Explanation
Cron, a concept originating from Unix-like operating systems, describes recurring schedules using a compact expression made of five fields representing minute, hour, day of month, month, and day of week respectively (some systems support a sixth, optional seconds field at the very start). For example, the expression '0 0 * * *' means 'run at minute 0 of hour 0, every day, every month, every day of the week', in other words, once daily at midnight. Understanding this syntax, even just recognizing common patterns, is valuable both for NestJS scheduling and for general backend development, since cron expressions appear across countless tools and platforms beyond NestJS itself.
NestJS's @nestjs/schedule package, after being imported via ScheduleModule.forRoot() in your root module, provides three decorators for different scheduling needs. @Cron(cronExpression) is the most flexible, letting you specify any recurring schedule using cron syntax (or a small set of convenient named constants like CronExpression.EVERY_DAY_AT_MIDNIGHT for common patterns, avoiding the need to memorize raw cron syntax for everyday cases). @Interval(milliseconds) is simpler, running a method repeatedly at a fixed time interval specified directly in milliseconds, useful when a straightforward 'every N minutes/seconds' schedule is all you need, without cron's more complex expressiveness. @Timeout(milliseconds) runs a method exactly once, a specified delay after the application starts, useful for a single, delayed startup task rather than anything recurring.
All three decorators are simply applied directly to a method within any @Injectable() service, and NestJS's dependency injection ensures that service can inject and use any other providers it needs, like a repository to query and delete expired records, exactly like any other method in your application.
A subtlety worth understanding, particularly relevant once an application scales beyond a single instance, is that @nestjs/schedule's scheduled tasks run independently within each running instance of your application. If you deploy multiple instances of the same NestJS application (common in production for redundancy and load distribution), each instance will independently execute its own scheduled tasks according to the same schedule, potentially causing a task intended to run 'once daily' to actually run once per instance, simultaneously, which may or may not be a problem depending on whether the task is safely idempotent (like a database cleanup query that simply deletes already-expired rows) or something that would cause issues if run multiple times concurrently (like sending a single daily summary email, which you would not want sent multiple times to the same recipient). For genuinely single-execution-across-all-instances scheduling needs, a distributed locking mechanism or a dedicated external scheduler is typically required beyond what @nestjs/schedule alone provides.
Interview-Friendly Explanation
A strong interview or viva answer for nestjs cron jobs tutorial must go beyond a one-line definition. Interviewers evaluating experienced backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters in production Node.js applications. Use the four-point framework below when answering under time pressure.
- Definition point: @nestjs/schedule: The official NestJS package providing decorators for scheduling recurring or delayed tasks.
- Working point: Schedule a recurring task using the @Cron() decorator and cron expression syntax.
- Example point: Cleaning up expired sessions, refresh tokens, or temporary uploaded files is one of the most common real-world uses of scheduled tasks, almost always run during low-traffic hours like midnight.
- Conclusion point: @nestjs/schedule brings task scheduling directly into NestJS's dependency-injection-aware architecture, letting scheduled methods use any injected service or repository naturally.
How to Answer This in a Technical Interview
- Give a two-to-three sentence definition using correct NestJS and Node.js terminology.
- Add one specific example drawn from a real backend scenario such as an e-commerce, fintech, or SaaS API.
- Mention a relevant trade-off or alternative approach, since interviewers often probe this contrast.
- Close with one benefit, limitation, or production use case.
- Avoid vague answers that only restate the term — interviewers filter these out immediately.
Practical Scenario
Imagine you are scaling a production backend for a SaaS product, similar to systems used at companies like Razorpay, Freshworks, or Zomato. Every lesson in this module maps directly to a decision you will make while making that backend more advanced, performant, and maintainable at scale.
NestJS CRON Jobs Tutorial: Architecture and Flow Diagram
Visualize a cron expression's five fields:
[Minute] [Hour] [Day of Month] [Month] [Day of Week]
0 0 * * * --> Every day at midnight
*/15 * * * * --> Every 15 minutes
0 9 * * 1-5 --> 9:00 AM, Monday through Friday
NestJS CRON Jobs Tutorial: Decorator Reference Table
| Decorator | Scheduling Style | Typical Use Case |
|---|---|---|
| @Cron(expression) | Flexible, cron-syntax-based recurring schedule | Daily cleanup jobs, weekday-only reports |
| @Interval(ms) | Simple, fixed-interval recurring schedule | Refreshing a cache every few minutes |
| @Timeout(ms) | Runs exactly once, after a startup delay | A single delayed initialization task |
NestJS CRON Jobs Tutorial: NestJS Code Example
// npm install @nestjs/schedule
// app.module.ts — enabling the schedule module
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [ScheduleModule.forRoot()],
})
export class AppModule {}
// tokens-cleanup.service.ts — a scheduled task cleaning up expired refresh tokens
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression, Interval } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThan } from 'typeorm';
import { RefreshToken } from './refresh-token.entity';
@Injectable()
export class TokensCleanupService {
private readonly logger = new Logger(TokensCleanupService.name);
constructor(
@InjectRepository(RefreshToken)
private readonly refreshTokenRepository: Repository<RefreshToken>,
) {}
// Runs automatically every day at midnight
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async removeExpiredTokens() {
const result = await this.refreshTokenRepository.delete({
expiresAt: LessThan(new Date()),
});
this.logger.log(`Cleaned up ${result.affected ?? 0} expired refresh tokens`);
}
// Runs every 30 minutes, useful for a lighter, more frequent check
@Interval(30 * 60 * 1000)
logHeartbeat() {
this.logger.log('TokensCleanupService heartbeat: still running');
}
}
ScheduleModule.forRoot(), imported into AppModule, activates NestJS's scheduling system application-wide. TokensCleanupService.removeExpiredTokens() is decorated with @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT), a convenient named constant sparing you from writing the raw cron string '0 0 * * *' directly, and uses its injected TypeORM repository to delete every refresh token whose expiresAt is earlier than the current time, exactly the kind of maintenance task real applications need to keep their database clean of stale, exercise-relevant data (revisiting the refresh token pattern from Module 4). logHeartbeat() demonstrates the simpler @Interval() decorator, logging a message every 30 minutes as a basic example of interval-based scheduling, distinct from cron's more expressive but also more complex syntax.
Real-World NestJS CRON Jobs Tutorial Industry Examples
- Cleaning up expired sessions, refresh tokens, or temporary uploaded files is one of the most common real-world uses of scheduled tasks, almost always run during low-traffic hours like midnight.
- SaaS platforms often use scheduled jobs to generate and send daily or weekly summary reports and digest emails to users at a consistent, predictable time.
- E-commerce platforms frequently schedule inventory reconciliation jobs, checking for discrepancies between recorded stock levels and actual warehouse data on a regular interval.
- Applications integrating with third-party APIs sometimes use scheduled tasks to periodically refresh cached external data (like exchange rates or shipping rates) that doesn't need to be fetched on every single request.
NestJS CRON Jobs Tutorial Interview Questions and Answers
Q1. How would you schedule a task to run automatically every day at midnight in NestJS?
Short answer: After importing ScheduleModule.forRoot() into a module, you would decorate a method within an @Injectable() service with @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) (or the equivalent raw cron expression '0 0 * * *'), and NestJS's scheduling system automatically invokes that method according to the specified schedule.
Detailed explanation: Cron, a concept originating from Unix-like operating systems, describes recurring schedules using a compact expression made of five fields representing minute, hour, day of month, month, and day of week respectively (some systems support a sixth, optional seconds field at the very start). For example, the expression '0 0 * * *' means 'run at minute 0 of hour 0, every day, every month, every day of the week', in other words, once daily at midnight. Understanding this syntax, even just recognizing common patterns, is valuable both for NestJS scheduling and for general backend development, since cron expressions appear across countless tools and platforms beyond NestJS itself. NestJS's @nestjs/schedule package, after being imported via ScheduleModule.forRoot() in your root module, provides three decorators for different scheduling needs. @Cron(cronExpression) is the most flexible, letting you specify any recurring schedule using cron syntax (or a small set of convenient named constants like CronExpression.EVERY_DAY_AT_MIDNIGHT for common patterns, avoiding the need to memorize raw cron syntax for everyday cases). @Interval(milliseconds) is simpler, running a method repeatedly at a fixed time interval specified directly in milliseconds, useful when a straightforward 'every N minutes/seconds' schedule is all you need, without cron's more complex expressiveness. @Timeout(milliseconds) runs a method exactly once, a specified delay after the application starts, useful for a single, delayed startup task rather than anything recurring. All three decorators are simply applied directly to a method within any @Injectable() service, and NestJS's dependency injection ensures that service can inject and use any other providers it needs, like a repository to query and delete expired records, exactly like any other method in your application. A subtlety worth understanding, particularly relevant once an application scales beyond a single instance, is that @nestjs/schedule's scheduled tasks run independently within each running instance of your application. If you deploy multiple instances of the same NestJS application (common in production for redundancy and load distribution), each instance will independently execute its own scheduled tasks according to the same schedule, potentially causing a task intended to run 'once daily' to actually run once per instance, simultaneously, which may or may not be a problem depending on whether the task is safely idempotent (like a database cleanup query that simply deletes already-expired rows) or something that would cause issues if run multiple times concurrently (like sending a single daily summary email, which you would not want sent multiple times to the same recipient). For genuinely single-execution-across-all-instances scheduling needs, a distributed locking mechanism or a dedicated external scheduler is typically required beyond what @nestjs/schedule alone provides.
Practical example: Cleaning up expired sessions, refresh tokens, or temporary uploaded files is one of the most common real-world uses of scheduled tasks, almost always run during low-traffic hours like midnight.
Interview tip: @nestjs/schedule requires ScheduleModule.forRoot() to be imported before its decorators take effect.
Revision hook: @nestjs/schedule brings task scheduling directly into NestJS's dependency-injection-aware architecture, letting scheduled methods use any injected service or repository naturally.
Q2. What is the difference between @Cron(), @Interval(), and @Timeout() in @nestjs/schedule?
Short answer: @Cron() schedules a method using flexible cron expression syntax, suitable for complex recurring patterns. @Interval() runs a method repeatedly at a fixed millisecond interval, simpler but less expressive than cron syntax. @Timeout() runs a method exactly once, after a specified delay following application startup, rather than repeatedly.
Detailed explanation: ScheduleModule.forRoot(), imported into AppModule, activates NestJS's scheduling system application-wide. TokensCleanupService.removeExpiredTokens() is decorated with @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT), a convenient named constant sparing you from writing the raw cron string '0 0 * * *' directly, and uses its injected TypeORM repository to delete every refresh token whose expiresAt is earlier than the current time, exactly the kind of maintenance task real applications need to keep their database clean of stale, exercise-relevant data (revisiting the refresh token pattern from Module 4). logHeartbeat() demonstrates the simpler @Interval() decorator, logging a message every 30 minutes as a basic example of interval-based scheduling, distinct from cron's more expressive but also more complex syntax.
Practical example: SaaS platforms often use scheduled jobs to generate and send daily or weekly summary reports and digest emails to users at a consistent, predictable time.
Interview tip: @Cron(expression) for flexible cron-based recurring schedules; CronExpression provides readable named constants.
Revision hook: Named cron constants like CronExpression.EVERY_DAY_AT_MIDNIGHT improve readability over memorizing raw cron syntax.
Q3. What problem can arise when using @nestjs/schedule in a multi-instance deployment?
Short answer: Since scheduled tasks run independently within each running instance of the application, a task intended to run 'once daily' would actually execute once per instance simultaneously in a multi-instance deployment, which can be problematic for non-idempotent tasks (like sending a single daily email) unless a distributed locking mechanism or external scheduler is used to coordinate execution across instances.
Detailed explanation: NestJS's official @nestjs/schedule package brings recurring and delayed task scheduling directly into the framework, using @Cron() for flexible, cron-expression-based schedules (often simplified using readable named constants like CronExpression.EVERY_DAY_AT_MIDNIGHT), @Interval() for simpler, fixed-millisecond recurring tasks, and @Timeout() for a single action delayed after startup. Applied directly to methods within an @Injectable() service, after ScheduleModule.forRoot() has been imported, scheduled tasks can naturally use dependency injection to access repositories or other services, exactly as demonstrated by a token cleanup job querying and deleting expired refresh tokens from Module 4. An important architectural consideration is that scheduled tasks run independently per application instance, meaning multi-instance deployments will execute the same scheduled task on every instance simultaneously, requiring either idempotent task design or external coordination for tasks that must run exactly once across the entire deployment.
Practical example: E-commerce platforms frequently schedule inventory reconciliation jobs, checking for discrepancies between recorded stock levels and actual warehouse data on a regular interval.
Interview tip: @Interval(ms) for simple, fixed-interval recurring tasks; @Timeout(ms) for a single delayed task.
Revision hook: Choosing between @Cron(), @Interval(), and @Timeout() depends on whether you need complex scheduling, simple repetition, or a single delayed action.
Q4. How would you write a cron expression for a job that should run every 15 minutes?
Short answer: The expression '*/15 * * * *' represents 'every 15th minute, every hour, every day, every month, every day of the week,' using the step syntax (*/15) in the minute field to specify an interval rather than a single fixed minute value.
Detailed explanation: Cron, a concept originating from Unix-like operating systems, describes recurring schedules using a compact expression made of five fields representing minute, hour, day of month, month, and day of week respectively (some systems support a sixth, optional seconds field at the very start). For example, the expression '0 0 * * *' means 'run at minute 0 of hour 0, every day, every month, every day of the week', in other words, once daily at midnight. Understanding this syntax, even just recognizing common patterns, is valuable both for NestJS scheduling and for general backend development, since cron expressions appear across countless tools and platforms beyond NestJS itself. NestJS's @nestjs/schedule package, after being imported via ScheduleModule.forRoot() in your root module, provides three decorators for different scheduling needs. @Cron(cronExpression) is the most flexible, letting you specify any recurring schedule using cron syntax (or a small set of convenient named constants like CronExpression.EVERY_DAY_AT_MIDNIGHT for common patterns, avoiding the need to memorize raw cron syntax for everyday cases). @Interval(milliseconds) is simpler, running a method repeatedly at a fixed time interval specified directly in milliseconds, useful when a straightforward 'every N minutes/seconds' schedule is all you need, without cron's more complex expressiveness. @Timeout(milliseconds) runs a method exactly once, a specified delay after the application starts, useful for a single, delayed startup task rather than anything recurring. All three decorators are simply applied directly to a method within any @Injectable() service, and NestJS's dependency injection ensures that service can inject and use any other providers it needs, like a repository to query and delete expired records, exactly like any other method in your application. A subtlety worth understanding, particularly relevant once an application scales beyond a single instance, is that @nestjs/schedule's scheduled tasks run independently within each running instance of your application. If you deploy multiple instances of the same NestJS application (common in production for redundancy and load distribution), each instance will independently execute its own scheduled tasks according to the same schedule, potentially causing a task intended to run 'once daily' to actually run once per instance, simultaneously, which may or may not be a problem depending on whether the task is safely idempotent (like a database cleanup query that simply deletes already-expired rows) or something that would cause issues if run multiple times concurrently (like sending a single daily summary email, which you would not want sent multiple times to the same recipient). For genuinely single-execution-across-all-instances scheduling needs, a distributed locking mechanism or a dedicated external scheduler is typically required beyond what @nestjs/schedule alone provides.
Practical example: Applications integrating with third-party APIs sometimes use scheduled tasks to periodically refresh cached external data (like exchange rates or shipping rates) that doesn't need to be fetched on every single request.
Interview tip: Scheduled tasks run per-instance; multi-instance deployments need care (or external coordination) for non-idempotent scheduled jobs.
Revision hook: Multi-instance deployments require deliberate thought about whether a scheduled task is safely idempotent before scaling horizontally.
NestJS CRON Jobs Tutorial MCQs and Practice Questions
1. Which official NestJS package provides task scheduling decorators like @Cron()?
- @nestjs/schedule
- @nestjs/throttler
- @nestjs/cache-manager
- @nestjs/config
Answer: A. @nestjs/schedule
Explanation: @nestjs/schedule is the official NestJS package providing @Cron(), @Interval(), and @Timeout() decorators for scheduling recurring or delayed tasks.
Concept link: @nestjs/schedule requires ScheduleModule.forRoot() to be imported before its decorators take effect.
Why this matters: @nestjs/schedule brings task scheduling directly into NestJS's dependency-injection-aware architecture, letting scheduled methods use any injected service or repository naturally.
2. What does the cron expression '0 0 * * *' represent?
- Every minute
- Every hour
- Once daily at midnight
- Once a week on Sunday
Answer: C. Once daily at midnight
Explanation: '0 0 * * *' specifies minute 0, hour 0, every day of month, every month, and every day of week, which together mean the task runs once per day at exactly midnight.
Concept link: @Cron(expression) for flexible cron-based recurring schedules; CronExpression provides readable named constants.
Why this matters: Named cron constants like CronExpression.EVERY_DAY_AT_MIDNIGHT improve readability over memorizing raw cron syntax.
3. Which decorator would you use for a task that should run repeatedly every fixed number of milliseconds, without needing cron syntax?
- @Cron()
- @Interval()
- @Timeout()
- @Scheduled()
Answer: B. @Interval()
Explanation: @Interval(milliseconds) runs a method repeatedly at a fixed time interval specified directly in milliseconds, offering a simpler alternative to cron expression syntax for straightforward recurring schedules.
Concept link: @Interval(ms) for simple, fixed-interval recurring tasks; @Timeout(ms) for a single delayed task.
Why this matters: Choosing between @Cron(), @Interval(), and @Timeout() depends on whether you need complex scheduling, simple repetition, or a single delayed action.
4. What potential issue can occur with @nestjs/schedule in a multi-instance deployment?
- Scheduled tasks never run at all
- Each instance runs its own copy of the scheduled task independently, potentially causing duplicate execution
- Cron expressions stop working entirely
- The application crashes on startup
Answer: B. Each instance runs its own copy of the scheduled task independently, potentially causing duplicate execution
Explanation: Since @nestjs/schedule's tasks run independently per application instance, multiple instances in a scaled deployment will each execute the same scheduled task according to the same schedule, which can cause unwanted duplicate execution for non-idempotent tasks.
Concept link: Scheduled tasks run per-instance; multi-instance deployments need care (or external coordination) for non-idempotent scheduled jobs.
Why this matters: Multi-instance deployments require deliberate thought about whether a scheduled task is safely idempotent before scaling horizontally.
Common NestJS CRON Jobs Tutorial Mistakes to Avoid
- Forgetting to import ScheduleModule.forRoot() into the application, causing @Cron(), @Interval(), and @Timeout() decorators to have no effect.
- Using a raw, hard-to-read cron expression when a named constant like CronExpression.EVERY_DAY_AT_MIDNIGHT already covers the same common pattern more clearly.
- Assuming a scheduled task will run exactly once across an entire multi-instance deployment, when in fact it runs independently, and potentially simultaneously, on every instance.
- Scheduling expensive, long-running tasks too frequently, potentially overlapping with a previous, still-running execution of the same task.
NestJS CRON Jobs Tutorial: Interview Notes and Exam Tips
- @nestjs/schedule requires ScheduleModule.forRoot() to be imported before its decorators take effect.
- @Cron(expression) for flexible cron-based recurring schedules; CronExpression provides readable named constants.
- @Interval(ms) for simple, fixed-interval recurring tasks; @Timeout(ms) for a single delayed task.
- Scheduled tasks run per-instance; multi-instance deployments need care (or external coordination) for non-idempotent scheduled jobs.
Key NestJS CRON Jobs Tutorial Takeaways
- @nestjs/schedule brings task scheduling directly into NestJS's dependency-injection-aware architecture, letting scheduled methods use any injected service or repository naturally.
- Named cron constants like CronExpression.EVERY_DAY_AT_MIDNIGHT improve readability over memorizing raw cron syntax.
- Choosing between @Cron(), @Interval(), and @Timeout() depends on whether you need complex scheduling, simple repetition, or a single delayed action.
- Multi-instance deployments require deliberate thought about whether a scheduled task is safely idempotent before scaling horizontally.
NestJS CRON Jobs Tutorial: Summary
NestJS's official @nestjs/schedule package brings recurring and delayed task scheduling directly into the framework, using @Cron() for flexible, cron-expression-based schedules (often simplified using readable named constants like CronExpression.EVERY_DAY_AT_MIDNIGHT), @Interval() for simpler, fixed-millisecond recurring tasks, and @Timeout() for a single action delayed after startup. Applied directly to methods within an @Injectable() service, after ScheduleModule.forRoot() has been imported, scheduled tasks can naturally use dependency injection to access repositories or other services, exactly as demonstrated by a token cleanup job querying and deleting expired refresh tokens from Module 4. An important architectural consideration is that scheduled tasks run independently per application instance, meaning multi-instance deployments will execute the same scheduled task on every instance simultaneously, requiring either idempotent task design or external coordination for tasks that must run exactly once across the entire deployment.