How to add alert notifications in your app, the easy way.

It’s such a beautifully, useful and simple thing that I had to write about it…

Sooner or later your app needs to tell you something: a CRON job failed, some queue is backing up, a quota is almost gone, a big customer just signed up. You don’t need a paid service, an SDK, or the joy of debugging email deliverability for this. A Slack Incoming Webhook is a secret URL you POST JSON to, and that’s the whole trick.

Step 1 – Create the webhook

In Slack: create an app (api.slack.com/apps > Create New App > From scratch), turn on Incoming Webhooks, then Add New Webhook to Workspace and pick a channel. You get back a URL like:

https://hooks.slack.com/services/T00000000/B00000000/xxxxxxxxxxxxxxxxxxxxxxxx

That URL is the credential. You don’t need anything else, no secrets, no IDs, nothing – those are for other Slack stuff. For “post a message to this channel”, the URL is enough.

Step 2 – Store it as a secret

Drop it in your environment, never in the repo:

# .env  (git-ignored)
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00000000/B00000000/xxxx"

Step 3 – A tiny reusable helper

A few lines of code, no deps, and – importantly – best-effort: meaning a notification that fails will not take your app down with it.

// notify.ts

type NotifyOpts = { 
  title?: string; 
  level?: "info" | "warn" | "error" 
};

const EMOJI = { 
  info: "ℹ️", 
  warn: "⚠️", 
  error: "🚨" 
} as const;

export async function notify(
  text: string, 
  opts: NotifyOpts = {}
): Promise<void> {
  const url = process.env.SLACK_WEBHOOK_URL;
  if (!url) return; // no-op, don't blow up

  const emoji = EMOJI[opts.level ?? "info"];
  const heading = opts.title 
    ? `${emoji} *${opts.title}*\n`
    : "";

  try {
    await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: heading + text }),
    });
  } catch (err) {
    console.error("notify failed (non-fatal):", err);
  }
}

Use it anywhere

await notify("Mr. Dude, stuff happened! ✅");

await notify(
  `DB usage is at ${percent}% of the plan cap.`, {
    title: "Quota warning",
    level: "warn",
  });

That’s a working alert system. Really.

Want it to look nicer?

Slack is really good at this stuff; the messages can carry Block Kit blocks for headings and compact key/value grids. Keep the top-level text too, it’s the fallback used in push notifications:

body: JSON.stringify({
  text: "Quota warning", // fallback
  blocks: [
    { 
      type: "section", 
      text: { 
        type: "mrkdwn", 
        text: "🚨 *Quota warning*" 
      } 
    },
    { type: "section", fields: [
      { type: "mrkdwn", text: "*Used:*\n84%" },
      { type: "mrkdwn", text: "*Plan:*\nScaler" },
    ]},
  ],
})

Two rules, and you’re done

  1. Best-effort, always. Wrap the send in try/catch and do a no-op when the URL is missing. Your alerting is the least important thing in any request; it must never be the thing that breaks it.
  2. The URL is a secret. Anyone who has it can post to your channel. Keep it out of git, and if it leaks, regenerate it in Slack (Incoming Webhooks > remove/add); no code changes, just swap the environment variable.

BTW, same pattern works for Discord and Telegram if Slack isn’t your thing; a different URL and a slightly different JSON body, basically same code; or use Python if you’re a guru :-).

I’m honestly wondering if there’s an easier way. I’d probably use it.

That’s it.
Enjoy!


Slack logo rendered with Krbn.

Comments

Don't keep it to yourself!…