SkillShare Logo
STEP 6 OF 7 · AUTOMATION

Low-Stock Alert Emails

The feature that makes this app useful: when a product’s quantity drops to its minStockLevel, the store owner automatically gets an email — no one has to remember to check.

01 — TWO STRATEGIES

When to Check

There are two complementary ways to catch low stock. Start with the first; add the second later if you like.

A

Event-driven (recommended)

Check right after every create/update. Instant alerts, zero extra infrastructure. This is the checkLowStock() we already call in the CRUD handlers.

B

Scheduled sweep (optional)

A cron job runs daily, finds everything currently below threshold, and sends a digest. A safety net for stock that dropped via other paths.

Add the right env var

Decide who receives alerts. Add STORE_OWNER_EMAIL="owner@store.com" to server/.env. This is the exact key the email service reads — if it's not set, it falls back to EMAIL_USER. Do not use ALERT_EMAIL, that key does not exist in this project.

02 — THE HELPER

The checkLowStock Function

A tiny function that compares the numbers and emails when needed. It reuses the same sendMail from the Authentication page.

server/src/controllers/productController.js
import { sendLowStockAlert } from './services/emailService.js'
// sendLowStockAlert is called from productController.js after every create/update:

const triggerLowStockAlert = (product) => {
  if (product.quantity <= product.minStockLevel) {
    sendLowStockAlert(product).catch((err) =>
      console.error('Failed to send low-stock alert:', err.message)
    )
  }
}

// Inside sendLowStockAlert (services/emailService.js):
// → sends to process.env.STORE_OWNER_EMAIL or EMAIL_USER as fallback
// → subject: '⚠️ Low Stock Alert: <product.name>' or 'U0001f6a8 Out of Stock: <product.name>'
// → shows product name, SKU, category, current quantity, and minStockLevel
Don't spam the inbox

As written, every update at-or-below the threshold sends an email. To send once per dip, add a lowStockNotified boolean to Product: set it true when you alert, and reset to false when a restock pushes quantity back above minStockLevel. Only email when it’s currently false.

03 — OPTIONAL

A Scheduled Daily Sweep

Optional enhancement — not in the base reference app. A backstop that emails a digest of everything currently low, running on a timer independent of user actions.

server/src/cron.js — npm install node-cron
import cron from 'node-cron'
import Product from '../models/Product.js'
import { sendLowStockAlert } from '../services/emailService.js'

// every day at 09:00 — a safety-net sweep
cron.schedule('0 9 * * *', async () => {
  const lowStock = await Product.find({
    $expr: { $lte: ['$quantity', '$minStockLevel'] },
  })

  for (const product of lowStock) {
    sendLowStockAlert(product).catch(console.error)
  }
})
// npm install node-cron
// Import this file once in server.js after connectDB()
Cron syntax

"0 9 * * *" = minute 0, hour 9, every day. Use crontab.guru to build your own schedule.

04 — CHECKPOINT

Test the Alert

Make a product go low and confirm the email lands in your Mailtrap inbox.

  1. Create a product with quantity: 5, minStockLevel: 5→ an alert should send immediately (5 ≤ 5).
  2. Or update an existing product’s quantity down to its minStockLevel and watch the inbox.
  3. Open Mailtrapand read the “⚠️ Low stock” email.
  • STORE_OWNER_EMAIL set in server/.env
  • triggerLowStockAlert() called in create + update handlers
  • Email arrives when quantity ≤ minStockLevel
  • No email when stock is healthy