<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Shahid</title>
    <description>The latest articles on DEV Community by Muhammad Shahid (@msal95).</description>
    <link>https://dev.to/msal95</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3904742%2F49c9cd22-9942-4cd5-8113-ae4033c20b51.jpg</url>
      <title>DEV Community: Muhammad Shahid</title>
      <link>https://dev.to/msal95</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/msal95"/>
    <language>en</language>
    <item>
      <title>Stop Writing Brevo Email Boilerplate — Use This Production-Grade Node.js Kit Instead</title>
      <dc:creator>Muhammad Shahid</dc:creator>
      <pubDate>Wed, 29 Apr 2026 17:36:20 +0000</pubDate>
      <link>https://dev.to/msal95/stop-writing-brevo-email-boilerplate-use-this-production-grade-nodejs-kit-instead-45o3</link>
      <guid>https://dev.to/msal95/stop-writing-brevo-email-boilerplate-use-this-production-grade-nodejs-kit-instead-45o3</guid>
      <description>&lt;p&gt;If you've ever integrated Brevo (formerly Sendinblue) into a Node.js or Next.js SaaS app, you already know the pain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You copy-paste transactional email logic across three projects&lt;/li&gt;
&lt;li&gt;Your webhook handler has no signature verification and breaks randomly&lt;/li&gt;
&lt;li&gt;Your "drip sequence" is three setTimeout calls held together with prayer&lt;/li&gt;
&lt;li&gt;GDPR right-to-erasure is a TODO comment from six months ago&lt;/li&gt;
&lt;li&gt;Every new dev on the team re-implements the same contact sync logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've shipped three SaaS products in production using Brevo. After extracting and refining the same patterns over and over, I packaged everything into &lt;a href="https://www.npmjs.com/package/brevo-saas-kit" rel="noopener noreferrer"&gt;brevo-saas-kit&lt;/a&gt; — a production-grade, zero-opinion email automation library for Node.js SaaS applications.&lt;/p&gt;

&lt;p&gt;This isn't a thin wrapper around the Brevo API. It's the actual email layer I use in production — with drip workflows, HMAC-verified webhooks, GDPR compliance, BullMQ queue support, and native Next.js App Router + Express integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactional emails&lt;/strong&gt;&lt;br&gt;
Welcome, verification, invoice, password reset, role change, account deletion&lt;br&gt;
&lt;strong&gt;Contact management&lt;/strong&gt;&lt;br&gt;
Create, update, tag, segment, bulk sync&lt;br&gt;
&lt;strong&gt;Automation workflows&lt;/strong&gt;&lt;br&gt;
Onboarding drip, trial reminders, re-engagement, upsell sequences&lt;br&gt;
&lt;strong&gt;Webhook handling&lt;/strong&gt;&lt;br&gt;
HMAC-SHA256 verification + typed event routing &lt;br&gt;
&lt;strong&gt;Next.js integration&lt;/strong&gt;&lt;br&gt;
Server actions (use server) + App Router webhook handler&lt;br&gt;
&lt;strong&gt;Express integration&lt;/strong&gt;&lt;br&gt;
Middleware + webhook router&lt;br&gt;
&lt;strong&gt;Queue system&lt;/strong&gt;&lt;br&gt;
Immediate or async BullMQ processing&lt;br&gt;
&lt;strong&gt;Analytics&lt;/strong&gt;&lt;br&gt;
Campaign stats, contact engagement scoring, CSV/JSON export&lt;br&gt;
&lt;strong&gt;Security&lt;/strong&gt;&lt;br&gt;
In-memory rate limiter, HTML sanitizer, API key validator&lt;br&gt;
&lt;strong&gt;GDPR&lt;/strong&gt;&lt;br&gt;
Right to erasure, signed consent logging, unsubscribe handler&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;br&gt;
npm install brevo-saas-kit&lt;/p&gt;

&lt;h1&gt;
  
  
  or
&lt;/h1&gt;

&lt;p&gt;yarn add brevo-saas-kit&lt;br&gt;
pnpm add brevo-saas-kit&lt;br&gt;
bun add brevo-saas-kit&lt;/p&gt;

&lt;h1&gt;
  
  
  Optional: async queue support
&lt;/h1&gt;

&lt;p&gt;npm install brevo-saas-kit bullmq&lt;/p&gt;

&lt;p&gt;Set your environment variables:&lt;br&gt;
BREVO_API_KEY=xkeysib-your-api-key&lt;br&gt;
BREVO_SENDER_EMAIL=&lt;a href="mailto:no-reply@yourdomain.com"&gt;no-reply@yourdomain.com&lt;/a&gt;&lt;br&gt;
BREVO_SENDER_NAME=YourApp&lt;br&gt;
BREVO_WEBHOOK_SECRET=your-webhook-secret&lt;/p&gt;

&lt;h1&gt;
  
  
  Contact list IDs from Brevo dashboard
&lt;/h1&gt;

&lt;p&gt;BREVO_LIST_FREE=1&lt;br&gt;
BREVO_LIST_PRO=2&lt;br&gt;
BREVO_LIST_ENTERPRISE=3&lt;/p&gt;

&lt;h1&gt;
  
  
  Only needed for async queue mode
&lt;/h1&gt;

&lt;p&gt;REDIS_URL=redis://localhost:6379&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: User Signup Flow (Next.js SaaS)&lt;/strong&gt;&lt;br&gt;
This is the most common real-world use case. User signs up → create Brevo contact → send welcome email → start onboarding drip.&lt;br&gt;
`// app/api/auth/signup/route.js&lt;br&gt;
import { createContact, sendWelcomeEmail, startOnboardingSequence } from 'brevo-saas-kit';&lt;/p&gt;

&lt;p&gt;export async function POST(req) {&lt;br&gt;
  const { email, name, plan } = await req.json();&lt;/p&gt;

&lt;p&gt;// Save to your DB first&lt;br&gt;
  const user = await db.users.create({ email, name, plan });&lt;/p&gt;

&lt;p&gt;// Brevo: create contact, tag with plan, add to right list&lt;br&gt;
  await createContact({&lt;br&gt;
    email,&lt;br&gt;
    name,&lt;br&gt;
    plan, // automatically adds to BREVO_LIST_FREE/PRO/ENTERPRISE&lt;br&gt;
    updateIfExists: true,&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;// Send welcome email&lt;br&gt;
  await sendWelcomeEmail({&lt;br&gt;
    email,&lt;br&gt;
    name,&lt;br&gt;
    dashboardUrl: &lt;code&gt;https://yourapp.com/dashboard&lt;/code&gt;,&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;// Start 5-step onboarding drip (Day 0, 1, 3, 7, 14)&lt;br&gt;
  await startOnboardingSequence({ email, name, mode: 'inline' });&lt;/p&gt;

&lt;p&gt;return Response.json({ success: true, userId: user.id });&lt;br&gt;
}`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Production Webhook Handling (Next.js App Router)&lt;/strong&gt;&lt;br&gt;
The #1 thing I see broken in Brevo integrations: webhooks with no signature verification. Anyone can POST to your endpoint and fake a bounce or unsubscribe event.&lt;br&gt;
brevo-saas-kit handles HMAC-SHA256 verification automatically:&lt;br&gt;
`// app/api/brevo/webhook/route.js&lt;br&gt;
import { createNextjsWebhookHandler } from 'brevo-saas-kit/integrations/nextjs/webhook';&lt;/p&gt;

&lt;p&gt;export const POST = createNextjsWebhookHandler({&lt;br&gt;
  secret: process.env.BREVO_WEBHOOK_SECRET,&lt;/p&gt;

&lt;p&gt;onDelivered: async ({ email, messageId }) =&amp;gt; {&lt;br&gt;
    await db.emails.update({ status: 'delivered' }, { where: { messageId } });&lt;br&gt;
  },&lt;/p&gt;

&lt;p&gt;onBounced: async ({ email, isHardBounce, reason }) =&amp;gt; {&lt;br&gt;
    if (isHardBounce) {&lt;br&gt;
      // Suppress future sends, notify your team&lt;br&gt;
      await db.users.update({ emailBounced: true }, { where: { email } });&lt;br&gt;
      await notifyTeam(&lt;code&gt;Hard bounce: ${email} — ${reason}&lt;/code&gt;);&lt;br&gt;
    }&lt;br&gt;
  },&lt;/p&gt;

&lt;p&gt;onUnsubscribed: async ({ email }) =&amp;gt; {&lt;br&gt;
    await db.users.update({ unsubscribed: true }, { where: { email } });&lt;br&gt;
  },&lt;/p&gt;

&lt;p&gt;onSpamComplaint: async ({ email }) =&amp;gt; {&lt;br&gt;
    // Immediately suppress — legal obligation in most jurisdictions&lt;br&gt;
    await db.users.update({ markedSpam: true }, { where: { email } });&lt;br&gt;
  },&lt;/p&gt;

&lt;p&gt;onError: async (error) =&amp;gt; {&lt;br&gt;
    console.error('[brevo-webhook]', error.message);&lt;br&gt;
    // Send to your error monitoring (Sentry, etc.)&lt;br&gt;
  },&lt;br&gt;
});`&lt;/p&gt;

&lt;p&gt;Every event is typed. No more if (event.event === 'hard_bounce') string matching across 200 lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3: Trial Expiry + Re-engagement (SaaS Lifecycle)&lt;/strong&gt;&lt;br&gt;
This is where most SaaS email integrations fall apart. You need timed sequences, not just one-off sends.&lt;/p&gt;

&lt;p&gt;Scenario 4: Invoice Emails (B2B SaaS)&lt;br&gt;
Stop attaching PDFs nobody reads. Send a clean, structured invoice email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 5: GDPR Right to Erasure&lt;/strong&gt;&lt;br&gt;
If you're selling to EU customers, this isn't optional. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 6: Express Middleware (REST APIs)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Scenario 7: Contact Engagement Analytics&lt;/strong&gt;&lt;br&gt;
Know which users are actually reading your emails before you trigger upsell campaigns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Not Just Use the Official @getbrevo/brevo SDK?&lt;/strong&gt;&lt;br&gt;
The official SDK is a raw API client. You still need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build every template yourself&lt;/li&gt;
&lt;li&gt;Write webhook signature verification from scratch&lt;/li&gt;
&lt;li&gt;Implement retry logic, rate limiting, error normalization&lt;/li&gt;
&lt;li&gt;Create your own drip/sequence orchestration&lt;/li&gt;
&lt;li&gt;Handle GDPR compliance manually&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://github.com/msal95/brevo-saas-kit" rel="noopener noreferrer"&gt;brevo-saas-kit&lt;/a&gt; is the production layer on top of that. It handles the patterns, not just the API calls. Think of it the same way you think of next-auth vs raw JWT — same relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;npm: &lt;a href="https://www.npmjs.com/package/brevo-saas-kit" rel="noopener noreferrer"&gt;npmjs.com/package/brevo-saas-kit&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/msal95/brevo-saas-kit" rel="noopener noreferrer"&gt;github.com/msal95/brevo-saas-kit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If this saved you time, a ⭐ on GitHub goes a long way. PRs, issues, and feedback welcome.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>saas</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
