Nestjs Cron
บทความนี้จะพาไปเรียนรู้วิธีการใช้งาน Cron ใน NestJS โดยใช้ @nestjs/schedule เพื่อจัดการงานแบบ scheduled อย่างมีประสิทธิภาพ ตั้งแต่การติดตั้ง @nestjs/schedule, การตั้งค่าโปรเจค NestJS, การติดตั้งแพ็กเกจที่จำเป็น ไปจนถึงการสร้างและใช้งาน cron พร้อมตัวอย่างโค้ดจริง เหมาะสำหรับผู้ที่ต้องการเพิ่มความสามารถในการประมวลผลงานแบบ scheduled ให้กับแอปพลิเคชันของตนเอง
ติดตั้ง nestjs-cron
nest new nestjs-cron
ติดตั้ง package
npm i @nestjs/schedule
สร้างโครงสร้างโปรเจค
src/
app.module.ts
main.ts
jobs/
jobs.module.ts
jobs.controller.ts
jobs.service.ts
ตั้งค่า
src/main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { JobsService } from "./jobs/jobs.service";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const jobs = app.get(JobsService);
// เพิ่ม cron แบบไดนามิก: ทุกนาที ณ เวลาเอเชีย/กรุงเทพ
jobs.addCronJob("reportEveryMinute", "* * * * *", "Asia/Bangkok");
await app.listen(3000);
}
bootstrap();
src/app.module.ts
import { Module } from "@nestjs/common";
import { ScheduleModule } from "@nestjs/schedule";
import { JobsModule } from "./jobs/jobs.module";
@Module({
imports: [
ScheduleModule.forRoot(), // เปิดระบบ scheduler ของ Nest
JobsModule, // รวม cron jobs ของเรา
],
})
export class AppModule {}
src/jobs/jobs.module.ts
import { Module } from "@nestjs/common";
import { JobsService } from "./jobs.service";
import { JobsController } from "./jobs.controller";
@Module({
controllers: [JobsController],
providers: [JobsService],
exports: [JobsService],
})
export class JobsModule {}
src/jobs/jobs.service.ts
/**
อธิบายโค้ด jobs.service.ts (NestJS + @nestjs/schedule)
โค้ดนี้เป็นตัวอย่างการใช้งาน Scheduler ใน NestJS เพื่อจัดการงานที่ต้องรันตามเวลา (Cron, Interval, Timeout) และรองรับการสร้าง/ลบ/หยุด cron job แบบไดนามิก
สรุปโครงสร้าง
- ใช้
@Injectable()เพื่อให้ class นี้ถูก inject ได้ในระบบ DI ของ NestJS - ใช้
Loggerสำหรับ log ข้อมูลต่าง ๆ - Inject
SchedulerRegistryเพื่อจัดการ job ที่รันอยู่ (เพิ่ม/ลบ/หยุด)
ฟีเจอร์หลัก
- Cron Job แบบ static
-> ใช้ decorator @Cron() เพื่อกำหนดให้ method handleHeartbeat รันทุก 30 วินาที (ตาม timezone Asia/Bangkok)
-> เหมาะกับงานที่ต้องรันเป็นระยะ ๆ เช่น health check, sync ข้อมูล ฯลฯ
- Interval Job
-> ใช้ decorator @Interval() ให้ method handleCleanup รันทุก 5 นาที
-> เหมาะกับงานที่ต้องรันแบบ fixed delay เช่น cleanup, clear cache
- Timeout Job
-> ใช้ decorator @Timeout() ให้ method seedOnce รันครั้งเดียวหลังจากบูต 10 วินาที
-> เหมาะกับงานที่ต้องทำครั้งเดียว เช่น seed ข้อมูล, prime cache
- Dynamic Cron Job
addCronJob(name, cronExpression, timeZone) เพิ่ม cron job ใหม่ขณะรันระบบ (runtime)
ตรวจสอบก่อนว่ามี job ชื่อนี้อยู่แล้วหรือไม่
สร้าง CronJob ใหม่และเพิ่มเข้า SchedulerRegistry
เริ่ม job ทันที
stopCronJob(name)หยุด job ชั่วคราวdeleteCronJob(name)ลบ job ออกจากระบบlistCronJobs()คืนชื่อ cron jobs ทั้งหมดที่มีในระบบ
จุดเด่น
- รองรับทั้ง static และ dynamic scheduling
- มีการ log ทุกขั้นตอนเพื่อ debug ได้ง่าย
- ใช้ type guard และ early return เพื่อป้องกัน error เช่น การเพิ่ม job ซ้ำชื่อ
- รองรับ timezone
เหมาะกับงานแบบไหน?
- งานที่ต้องรันตามเวลาหรือเป็นระยะ ๆ (เช่น report, sync, health check)
งานที่ต้องการเพิ่ม/ลบ/หยุด schedule ขณะระบบกำลังรัน (dynamic)
อ้างอิง
import { Injectable, Logger } from "@nestjs/common";
import {
Cron,
CronExpression,
Interval,
Timeout,
SchedulerRegistry,
} from "@nestjs/schedule";
import { CronJob } from "cron";
@Injectable()
export class JobsService {
private readonly logger = new Logger(JobsService.name);
constructor(private readonly schedulerRegistry: SchedulerRegistry) {}
/**
* รันทุก ๆ 30 วินาที (ตามเวลา Asia/Bangkok)
* ใช้ชื่อ job = 'heartbeat'
*/
@Cron(CronExpression.EVERY_30_SECONDS, {
name: "heartbeat",
timeZone: "Asia/Bangkok",
})
handleHeartbeat() {
// เขียน logic ที่อยากรันเป็นระยะ
this.logger.debug("Heartbeat: ping…");
}
/**
* ตัวอย่าง Interval – รันทุก 5 นาที
* (เหมาะกับงานที่เป็น fixed delay)
*/
@Interval("cleanup", 5 * 60 * 1000)
handleCleanup() {
this.logger.log("Cleanup running every 5 minutes");
// … ทำความสะอาดแคช/ไฟล์/สเตตัส ฯลฯ
}
/**
* ตัวอย่าง Timeout – รันครั้งเดียวหลังจากบูต 10 วินาที
* (เหมาะกับงาน seed/prime cache ครั้งเดียว)
*/
@Timeout("seedOnce", 10_000)
seedOnce() {
this.logger.warn("Seeding data once after app start");
// … ทำงานครั้งเดียว
}
/**
* ===== งานแบบไดนามิก (สร้าง/หยุด/ลบระหว่างรันได้) =====
*/
// เพิ่ม cron job แบบกำหนด expression เอง
addCronJob(name: string, cronExpression: string, timeZone = "Asia/Bangkok") {
if (this.schedulerRegistry.doesExist("cron", name)) {
this.logger.warn(`Cron job "${name}" already exists. Skipped.`);
return;
}
const job = new CronJob(
cronExpression,
() => {
this.logger.log(`[${name}] tick at ${new Date().toISOString()}`);
// … ใส่ logic ของงานนี้
},
null,
false, // start ตอนสร้างหรือไม่ (เราเลือก false ไว้ก่อน)
timeZone
);
// To fix type incompatibility, cast job to 'any' when adding to schedulerRegistry
this.schedulerRegistry.addCronJob(name, job as any);
job.start();
this.logger.log(
`Added & started dynamic cron "${name}" (${cronExpression}, ${timeZone})`
);
}
// หยุด job ชั่วคราว
stopCronJob(name: string) {
const job = this.schedulerRegistry.getCronJob(name);
job.stop();
this.logger.warn(`Stopped cron "${name}"`);
}
// ลบ job ออกไปเลย
deleteCronJob(name: string) {
this.schedulerRegistry.deleteCronJob(name);
this.logger.warn(`Deleted cron "${name}"`);
}
// ดูรายการชื่อ cron ทั้งหมด
listCronJobs(): string[] {
const names = this.schedulerRegistry.getCronJobs().keys();
return Array.from(names);
}
}
src/jobs/jobs.controller.ts
อธิบายโค้ด JobsController
JobsController คือ NestJS Controller ที่ใช้จัดการและควบคุม dynamic cron jobs ผ่าน HTTP endpoint ต่าง ๆ โดยแต่ละ endpoint จะเรียกใช้เมธอดของ JobsService เพื่อดำเนินการกับ cron jobs ดังนี้
@Get('add/:name/:expr')
สร้างและเริ่ม cron job ใหม่ตามชื่อ (name) และ cron expression (expr) ที่รับมาจาก URL parameter โดยจะเรียกaddCronJobของ JobsService ถ้าสร้างสำเร็จจะคืนชื่อ jobs ทั้งหมดกลับไป@Get('stop/:name')
หยุดการทำงานของ cron job ตามชื่อที่ระบุ โดยเรียกstopCronJobของ JobsService@Get('delete/:name')
ลบ cron job ตามชื่อที่ระบุ โดยเรียกdeleteCronJobของ JobsService และคืนชื่อ jobs ที่เหลือกลับไป@Get()
คืนรายการชื่อ cron jobs ทั้งหมดที่มีอยู่ในระบบ
Controller นี้ช่วยให้สามารถจัดการ cron jobs ได้แบบ dynamic ผ่าน REST API เช่น การเพิ่ม, หยุด, ลบ, และดูรายการ jobs โดยไม่ต้องแก้ไขโค้ดหรือรีสตาร์ทเซิร์ฟเวอร์ เหมาะสำหรับงานที่ต้องการความยืดหยุ่นในการตั้งเวลาและควบคุม background jobs ในแอปพลิเคชัน NestJS
import { Controller, Get, Param } from "@nestjs/common";
import { JobsService } from "./jobs.service";
@Controller("jobs")
export class JobsController {
constructor(private readonly jobs: JobsService) {}
@Get("add/:name/:expr")
add(@Param("name") name: string, @Param("expr") expr: string) {
this.jobs.addCronJob(name, expr);
return { ok: true, jobs: this.jobs.listCronJobs() };
}
@Get("stop/:name")
stop(@Param("name") name: string) {
this.jobs.stopCronJob(name);
return { ok: true };
}
@Get("delete/:name")
del(@Param("name") name: string) {
this.jobs.deleteCronJob(name);
return { ok: true, jobs: this.jobs.listCronJobs() };
}
@Get()
list() {
return this.jobs.listCronJobs();
}
}
ทดสอบการทำงาน
curl -X POST http://localhost:3000/jobs/add/heartbeat/0/0 * * * * *
curl -X POST http://localhost:3000/jobs/stop/heartbeat
curl -X POST http://localhost:3000/jobs/delete/heartbeat
curl -X POST http://localhost:3000/jobs/list
สรุป
- NestJS Scheduler (
@nestjs/schedule) ช่วยให้สามารถตั้งเวลา (Cron, Interval, Timeout) สำหรับรันงาน background ได้อย่างยืดหยุ่น - สามารถกำหนด timezone, ตั้งชื่อ job, และควบคุมการทำงาน (เพิ่ม/หยุด/ลบ) job ได้แบบ dynamic ผ่าน
SchedulerRegistry - การแยก logic งาน background ออกเป็น service เฉพาะ ช่วยให้โค้ดสะอาดและดูแลรักษาง่าย
- สามารถสร้าง REST API สำหรับจัดการ job ได้ เช่น เพิ่ม, หยุด, ลบ, และดูรายการ job ทั้งหมด
- เหมาะกับงานที่ต้องการรันอัตโนมัติ เช่น ส่งอีเมล, ทำความสะอาดข้อมูล, หรือ sync ข้อมูลตามเวลาที่กำหนด
ตัวอย่างโค้ดนี้สามารถนำไปต่อยอดกับงาน production ได้ทันที และรองรับการขยายฟีเจอร์ในอนาคต
สามารถดูตัวอย่างโค้ดและโปรเจกต์เต็มได้ที่ GitHub: https://github.com/earth774/nestjs-cron