Home  /  Blog  /  Prayer Times Notifications with Node.js

Prayer Times Push Notifications with Node.js

Node.js8 min readSeptember 2026

This post shows you how to build a Node.js service that fetches prayer times from UmmahAPI, handles timezones correctly, and sends web push notifications to users at each prayer time.

What you will build

By the end you will have a Node.js script that runs daily, pulls prayer times for a given location, and schedules a push notification for each of the five prayer times. The stack is simple: node-cron for scheduling, web-push for sending notifications, and luxon for timezone-safe time parsing. No database is required for this example, though you can add one later to store user subscriptions.

Install the dependencies first:

npm install node-cron web-push luxon

Fetch today's prayer times

The UmmahAPI prayer times endpoint returns Fajr, Sunrise, Dhuhr, Asr, Maghrib, and Isha for any latitude and longitude. Here is a simple fetch function:

// prayerTimes.js
import fetch from 'node-fetch';

const API_BASE = 'https://ummahapi.com/api';
const API_KEY  = process.env.UMMAH_API_KEY; // optional, but recommended

async function fetchPrayerTimes(lat, lng) {
  const url = `${API_BASE}/prayer-times?lat=${lat}&lng=${lng}`;
  const headers = API_KEY ? { 'x-api-key': API_KEY } : {};

  const res  = await fetch(url, { headers });
  if (!res.ok) throw new Error(`Prayer times fetch failed: ${res.status}`);

  const data = await res.json();
  return data.timings; // { Fajr, Sunrise, Dhuhr, Asr, Maghrib, Isha }
}

export { fetchPrayerTimes };

The response object contains a timings key with each prayer name mapped to a time string like "05:12". These times are in the local time for the coordinates you passed. That brings us to the most important part: timezones.

Tip. Register for a free API key at ummahapi.com/register to get unlimited requests. The keyless tier gives you 5,000 requests per 15 minutes, which is plenty for testing.

Handling timezones correctly

The times returned by UmmahAPI are local times. If your server runs in UTC (as most cloud servers do), you cannot just parse "05:12" with new Date(). You need to attach the correct IANA timezone for the user's location before converting to UTC.

Use luxon to build a DateTime in the target timezone, then convert it to UTC:

// timezone.js
import { DateTime } from 'luxon';

/**
 * Convert a prayer time string like "05:12" to a UTC Date object.
 * timezone: IANA string, e.g. "Asia/Riyadh" or "America/New_York"
 */
function prayerTimeToUTC(timeStr, timezone) {
  const [hour, minute] = timeStr.split(':').map(Number);
  const now = DateTime.now().setZone(timezone);

  const localDT = now.set({ hour, minute, second: 0, millisecond: 0 });
  return localDT.toUTC().toJSDate(); // plain JS Date in UTC
}

export { prayerTimeToUTC };

You need to know the user's IANA timezone. If you store it with their subscription (recommended), you can pass it directly. A common approach is to ask for it on the frontend with Intl.DateTimeFormat().resolvedOptions().timeZone and save it alongside the push subscription object.

Set up web push

Web Push requires VAPID keys. Generate them once:

import webpush from 'web-push';

const vapidKeys = webpush.generateVAPIDKeys();
console.log(vapidKeys.publicKey);
console.log(vapidKeys.privateKey);

Save the output to environment variables, then configure web-push at startup:

// push.js
import webpush from 'web-push';

webpush.setVapidDetails(
  'mailto:[email protected]',
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

async function sendPushNotification(subscription, payload) {
  try {
    await webpush.sendNotification(subscription, JSON.stringify(payload));
  } catch (err) {
    // 410 means the subscription is gone, remove it from your store
    console.error('Push failed:', err.statusCode, err.message);
  }
}

export { sendPushNotification };

The subscription object is the one the browser gives you after the user accepts notifications. It contains an endpoint and encryption keys. Store it in a database or a JSON file for this example.

Schedule notifications for each prayer

Now put it together. The script below runs once a day at midnight (in the server's timezone), fetches prayer times, converts each time to UTC, then uses setTimeout to fire a push notification at the right moment.

// scheduler.js
import cron from 'node-cron';
import fs from 'fs';
import { fetchPrayerTimes } from './prayerTimes.js';
import { prayerTimeToUTC } from './timezone.js';
import { sendPushNotification } from './push.js';

// Load subscriptions from a simple JSON file for this example
function loadSubscriptions() {
  const raw = fs.readFileSync('./subscriptions.json', 'utf8');
  return JSON.parse(raw); // array of { subscription, lat, lng, timezone }
}

const PRAYERS = ['Fajr', 'Dhuhr', 'Asr', 'Maghrib', 'Isha'];

async function scheduleDayNotifications() {
  const users = loadSubscriptions();

  for (const user of users) {
    const { subscription, lat, lng, timezone } = user;
    const timings = await fetchPrayerTimes(lat, lng);

    for (const prayer of PRAYERS) {
      const timeStr = timings[prayer];
      if (!timeStr) continue;

      const fireAt  = prayerTimeToUTC(timeStr, timezone);
      const msUntil = fireAt.getTime() - Date.now();

      if (msUntil < 0) continue; // prayer already passed today

      setTimeout(async () => {
        await sendPushNotification(subscription, {
          title: `${prayer} time`,
          body:  `It is time for ${prayer} prayer.`,
          icon:  '/icons/prayer-icon-192.png'
        });
      }, msUntil);
    }
  }
}

// Run at 00:01 every day (server local time)
cron.schedule('1 0 * * *', () => {
  console.log('Scheduling prayer notifications for today...');
  scheduleDayNotifications().catch(console.error);
});

// Also run immediately on startup so today is covered
scheduleDayNotifications().catch(console.error);
console.log('Prayer notification scheduler running.');

The if (msUntil < 0) continue check skips prayers that have already passed for the day. This matters when the server restarts mid-day.

Tip. If you support many users across different locations, batch your API calls. Fetch prayer times per unique coordinate pair, not per user. Most users in the same city will share identical times.

The service worker side

Web push only works if the browser has a registered service worker. Here is a minimal service worker that shows the notification:

// public/sw.js
self.addEventListener('push', function(event) {
  const data = event.data ? event.data.json() : {};
  const title   = data.title || 'Prayer Time';
  const options = {
    body: data.body || 'Time to pray.',
    icon: data.icon || '/icons/prayer-icon-192.png',
    badge: '/icons/badge-72.png'
  };
  event.waitUntil(self.registration.showNotification(title, options));
});

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(clients.openWindow('/'));
});

Register it on the frontend and subscribe the user. Then POST the subscription object to your server along with their latitude, longitude, and timezone string. Save that to your subscriptions.json file (or a real database):

// Frontend: subscribe.js
async function subscribeToPrayerNotifications(lat, lng) {
  const reg = await navigator.serviceWorker.register('/sw.js');
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array('YOUR_VAPID_PUBLIC_KEY')
  });

  await fetch('/api/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      subscription: sub,
      lat,
      lng,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
    })
  });
}

// Helper to convert VAPID key
function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64  = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const rawData = atob(base64);
  return Uint8Array.from([...rawData].map(c => c.charCodeAt(0)));
}

Fetching a full month at once

Instead of calling the API every day, you can pull an entire month of prayer times in one request. The UmmahAPI endpoint accepts month and year query parameters:

async function fetchMonthlyPrayerTimes(lat, lng, month, year) {
  const url = `https://ummahapi.com/api/prayer-times?lat=${lat}&lng=${lng}&month=${month}&year=${year}`;
  const res  = await fetch(url);
  const data = await res.json();
  return data; // array of daily timing objects
}

You could cache this result and serve daily times from memory instead of making a new API call each night. This reduces outbound requests and makes your scheduler faster at startup.

Running in production

A few things to handle before going live:

For more details on the prayer times response format and available calculation methods, see the full API documentation or the prayer times API page.

FAQ

How do I get prayer times for a specific location in Node.js?

Call the UmmahAPI endpoint with latitude and longitude: GET https://ummahapi.com/api/prayer-times?lat=YOUR_LAT&lng=YOUR_LNG. The response includes a timings object with Fajr, Sunrise, Dhuhr, Asr, Maghrib, and Isha.

What Node.js packages do I need for prayer times push notifications?

You need node-cron for scheduling, web-push for sending Web Push notifications, and luxon for timezone-safe time parsing. Install all three with: npm install node-cron web-push luxon.

How do I handle timezones when scheduling prayer time notifications?

Prayer times from UmmahAPI are in local time for the given coordinates. Use luxon to parse those times in the correct IANA timezone string (for example "America/New_York"), then convert to UTC before passing the value to setTimeout. Collect the timezone from the browser using Intl.DateTimeFormat().resolvedOptions().timeZone when the user subscribes.

Do I need an API key to use the UmmahAPI prayer times endpoint?

No key is required to start. The free tier allows 5,000 requests per 15 minutes. For unlimited requests, register for a free API key at ummahapi.com/register.

Build something with UmmahAPI

UmmahAPI is a free Islamic API with prayer times, Quran, Hadith, Duas, Qibla direction, and more.

Read the docs