How We Implemented Personalized Dynamic Reminders in Node.js Using node-schedule

In many apps, users expect notifications exactly when they need them—like Google Meet reminders. In our Node.js app, we faced the challenge of sending dynamic, user-specific reminders at the precise times chosen by users.

At first, we considered running a cron job every few minutes to check for due reminders. But this approach quickly became inefficient and unscalable: it constantly queried the database, caused delays, and struggled to handle a growing number of users.

After some research, we found the perfect solution: node-schedule combined with luxon. This approach allows us to schedule reminders dynamically, handle timezones, and efficiently manage one-time or repeating notifications.


Scheduling a Reminder

We create a scheduled job for each reminder using node-schedule, converting user-selected times to the correct timezone with luxon:

import schedule, { Job } from "node-schedule";
import { DateTime } from "luxon";
class ReminderScheduler {
  private jobs: Map<string, Job> = new Map();
  scheduleReminder(reminder) {
    const reminderDate = DateTime.fromJSDate(reminder.reminderDate)
                                 .setZone("Europe/Dublin")
                                 .toJSDate();
    this.cancelReminder(reminder._id.toString());
    const job = schedule.scheduleJob(reminder._id.toString(), reminderDate, async () => {
      console.log(`Reminder triggered: ${reminder.name}`);
      await this.handlePostTriggerUpdate(reminder);
    });
    this.jobs.set(reminder._id.toString(), job);
    console.log(`Scheduled reminder ${reminder._id} at ${reminderDate.toISOString()}`);
  }

Post-Trigger Update

After a reminder is triggered, we update its status. If the reminder repeats daily or weekly, we automatically schedule the next occurrence:

  private async handlePostTriggerUpdate(reminder) {
    if (!reminder.isOneTimeReminder) {
      const next = DateTime.fromJSDate(reminder.reminderDate)
                           .plus({ days: reminder.repeatDuration === "Daily" ? 1 : 7 });
      reminder.reminderDate = next.toJSDate();
      this.scheduleReminder(reminder);
    } else {
      reminder.isActive = false;
    }
    console.log(`Post-trigger update done for ${reminder._id}`);
  }

**Canceling a Reminder
**
We also allow reminders to be canceled or rescheduled easily:

 cancelReminder(reminderId: string) {
    const job = this.jobs.get(reminderId);
    if (job) {
      job.cancel();
      this.jobs.delete(reminderId);
      console.log(`Canceled reminder job ${reminderId}`);
    }
  }
}

Why This Approach Works

  • Users receive notifications exactly when they want them, without inefficient polling.

  • Supports timezones with Luxon.

  • Handles one-time, daily, and weekly reminders reliably.

  • Scalable and maintainable for a growing user base.


Summary
Using node-schedule with luxon allows your Node.js app to provide dynamic, personalized reminders that are reliable and efficient. It’s a simple yet powerful way to implement real-time notifications for any app.