目次
はじめに
Nestjs
を使ったプロジェクトで既に予約機能が用意されているところに追加で、その予約をしたことを忘れないようにユーザーに対して 30 分前にリマインドの通知を送りたい。という課題があったので、バックエンドの経験が浅い私はどう実装しようかと悩みながら調べてみたところ、Nestjs での方でそういったリマインダーの機能が用意されていたので、これを使って実装をして学んだことをドキュメントの例を交えながら、まとめていきます。
Nestjs のリマインダー機能とは?
私は冒頭でリマインダー機能と呼んでいましたが、ドキュメントの方ではTask Scheduling
と呼ばれているようです。
基本的に実行できるタイミングはこちら
- 固定した日時
- 定期的な間隔
- 指定した間隔を空けて 1 度
それ以外にも、今回実現したい機能のように動的なタイミングで実行できるDynamic API
なるものが用意されています。
具体的には@nestjs/schedule
というパッケージを利用して実装していきます。
パッケージのインストール
yarn add @nestjs/schedule
で OK。
基本をなめる
まずは基本的にどのような使い方をしていくのかを見ていきましょう!
Module
に @nestjs/schedule
からScheduleModule
をimport
していきます。
import { Module } from '@nestjs/common'
import { ScheduleModule } from '@nestjs/schedule'
@Module({
imports: [ScheduleModule.forRoot()], // forRootメソッドを実行
controllers: [RemindController],
providers: [RemindService],
})
export class RemindModule {}
forRoot
メソッドを実行することでスケジュールの初期処理が走って、これから紹介するデコレータが登録されます。
@Cron
@Interval
@Timeout
以下で簡単にまとめる
@Cron
指定したタイミングで処理を実行します。
- 指定した日時に 1 度
- 指定した定期的なタイミング(1 時間に 1 回、1 週間に 1 回、5 分に 1 回など)
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
@Injectable()
export class TasksService {
private readonly logger = new Logger(TasksService.name);
@Cron('45 * * * * *') // タイミングを指定したいメソッドにデコレータとして設定
handleCron() {
this.logger.debug('Called when the current second is 45');
}
}
上記のコードでは毎分 45 秒になったタイミングで実行される。
オプションが設定できたりといろいろ設定ができたりするのですが、今回は Cron の引数の45 * * * * *
ところだけ取り上げます。
* * * * * *
| | | | | |
| | | | | day of week
| | | | months
| | | day of month
| | hours
| minutes
seconds (optional)
*
に月・週・曜日・時間・分・秒を当てはめてタイミングを調整できるようですね。
曜日とか細かく設定できるのは便利そうです。
また、JS のDate
オブジェクトを受け入れることができるので、ここで動的にリマインドする機能を実装できるかも?と思ったのですが、これから説明するDynamic API
の方がさまざまなメソッドが用意されていたり管理しやすいな。という印象だし、元々動的に管理する目的のものなのでこちらで実装するのが正しそうです。
@Interval
定期的に指定された間隔で実行するもの
JS のsetInterval
のイメージ
@Interval(10000)
handleInterval() {
this.logger.debug('Called every 10 seconds');
}
@Timeout
指定したタイミング 1 度だけ実行するもの
JS のsetTimeout
のイメージ
@Timeout(5000)
handleTimeout() {
this.logger.debug('Called once after 5 seconds');
}
動的なタイミングでリマインドをつくる
お待たせしました。
ここからが本題です。
Dynamic API
を使って、いざリマインド処理を実装していきます!
簡単に説明すると、先ほど説明した@Cron
,@Interval
,@Timeout
をを文字通り動的に管理することができるものです。
今回は予約が入ったタイミングでのリマインドになるので、@Cron
を使って実装をしていきます。
予約が入ったタイミングでタスクをつくっていくのに CronJob
を使います。
yarn add cron
をしてパッケージをインストールします。
import { CronJob } from 'cron'
そして、そのタスクを管理するのに@nest/schedule
からSchedulerRegistry
を使ってスケジュールされたタスクの管理をしていきます。
import { CronJob } from 'cron';
import { SchedulerRegistry } from '@nestjs/schedule';
@Injectable()
export class RemindService {
constructor(private schedulerRegistry: SchedulerRegistry) {}
}
まずこれで簡単に CronJob の作成とそこにアクセスしてみます。
import { CronJob } from 'cron';
import { SchedulerRegistry } from '@nestjs/schedule';
@Injectable()
export class RemindService {
constructor(private schedulerRegistry: SchedulerRegistry) {}
@Cron('* * 4 * * *', {
name: 'remind', // ここで固有の値を設定
})
triggerReminds() {}
getTasks() {
const job = this.schedulerRegistry.getCronJob('remind'); // triggerRemindsでnameとして指定した"remind"のタスクたちを取得
job.stop();
console.log(job);
}
}
作成したタスク(job
)ではこんなことができます。
stop()
実行がスケジュールされているジョブを停止しますstart()
停止されたジョブを再開します。setTime(time: CronTime)
ジョブを停止し、新しい時刻を設定してから開始しますlastDate()
ジョブが実行された最後の日付の文字列表現を返しますnextDates(count: number)countmoment
今後のジョブ実行日を表すオブジェクトの配列 (size )を返します
リマインド機能
すみません、それでは本当に本題の予約が入ったらリマインドを入れる機能を実装していきます。
import { CronJob } from 'cron';
import { SchedulerRegistry } from '@nestjs/schedule';
@Injectable()
export class RemindService {
constructor(private schedulerRegistry: SchedulerRegistry) {}
reservationRemind(data) { // dataは予約データが入る想定
// 予約日時の30分前
const jobTime = new Date(data.dateAndTime .getTime() - 30 * 60 * 1000);
// 予約固有の値
const jobName = data.uuid;
// タスクの作成
const job = new CronJob(jobTime, () => {
// ここで例えばメールで通知を送るメソッドなどを実行
this.sendRemindMail(data, jobName)
})
// リマインド
this.schedulerRegistry.addCronJob(jobName, job)
// 実行
job.start()
// タスクが追加されたか確認
console.log(this.schedulerRegistry.getCronJobs());
}
sendRemindMail() {
// メールを送る処理
~~~
// 完了したタスクを削除
this.schedulerRegistry.deleteCronJob(cronName);
}
}
これで完成!!!
実装自体は非常にシンプルでしたね。
参考
さいごに
nest/schedule
を使ってのリマインド機能の実装おもしろかったですね。
理解して使いこなすことができれば、もっとさまざまな要件でも実装することができるようになると思うので、またそんなケースがあったらこちらで共有していきたいなと思います。
ありがとうございました!