Frequently Asked Questions
Build a Next.js application with API routes to handle campaign creation and a Node.js worker process using BullMQ to manage the message queue. This worker interacts with the Plivo API to send messages based on jobs added to the queue, ensuring reliable delivery even with large recipient lists. The provided guide details project setup, implementation, and deployment steps.
BullMQ is used as a robust message queue system built upon Redis. It handles the potentially time-consuming task of sending numerous SMS/MMS messages reliably and without blocking the main application thread. This ensures scalability and prevents message loss due to server issues or API limitations.
Prisma, a next-generation ORM (Object-Relational Mapper), simplifies database management within the Node.js/TypeScript backend. It provides type-safe database queries, schema migrations, and efficient database operations, facilitating easier interaction with the PostgreSQL database used in this project.
The application's API endpoint accepts a `scheduledAt` parameter during campaign creation. This parameter expects an ISO 8601 formatted date-time string. If provided, the campaign's messages will be queued in BullMQ with a delay, processed by the worker at the specified future time.
Redis functions as the in-memory data store backing the BullMQ message queue. It ensures efficient and reliable storage of queued message jobs until they are processed and sent by the worker. Its speed and persistence make it ideal for handling asynchronous tasks like sending messages.
The system includes an `OptOut` model in the database to record opted-out phone numbers. Before sending any message, the application checks the recipient number against this list to ensure compliance and prevent unwanted messages. The implementation details are provided in the article's API endpoint section.
Install the `plivo-node` SDK and initialize the Plivo client in a helper function. In the BullMQ job worker, use the Plivo client to create and send SMS/MMS messages based on the queued job data which includes recipient numbers and message content. The guide provides setup and implementation code snippets.
Install Node.js and NPM, create a Next.js project, install dependencies like 'plivo-node', 'prisma', 'bullmq', configure environment variables with Plivo and database credentials, and set up local database instances using Docker. The provided article outlines each step in detail.
Basic message statuses (e.g., SENT, FAILED) are tracked for each recipient in the database. The guide also includes a section on implementing webhooks to receive more detailed statuses like DELIVERED/UNDELIVERED from Plivo if you want real-time updates directly from Plivo.
Whenever your SMS sending volume is large enough that sending each message individually within the request cycle could lead to timeouts or performance issues, a message queue like BullMQ is highly recommended. It allows the system to process the requests asynchronously, improving performance and reliability.
Yes, this system supports sending MMS (multimedia) messages. You can add an array of media URLs to the request data and the Plivo client will send them as an MMS message. The `mediaUrls` field in the `Campaign` and `SmsJobData` structures enables MMS handling.
The article provides a database schema that includes an `OptOut` table. The implementation of adding numbers to this table is not explicitly covered in the article, but typically would involve creating an API endpoint or interface to record opt-out requests from users (e.g., SMS reply with 'STOP', web form).
This project leverages Next.js for the frontend and API routes, Node.js for the runtime, Plivo for sending SMS/MMS, Prisma for database interaction with PostgreSQL, Redis with BullMQ for message queuing, and optional tools like Tailwind CSS for styling and Docker for local development.
This guide provides a complete walkthrough for building a robust system to send SMS and MMS marketing campaigns using Plivo's communication APIs, powered by a Next.js frontend and Node.js backend logic (leveraging Next.js API routes).
We'll cover everything from project setup and core implementation to deployment, security, error handling, and monitoring, enabling you to create a scalable and reliable marketing communication platform.
Project Goal: To build a web application where users can:
Problem Solved: Manually sending marketing messages is inefficient and error-prone. This system automates the process, enables personalization (though basic in this guide), handles potentially large lists, and leverages a reliable provider like Plivo for deliverability.
Technologies Used:
System Architecture:
(Note: Ensure your publishing platform correctly renders Mermaid syntax, or provide a static image fallback with appropriate alt text.)
Prerequisites:
Final Outcome: A functional web application deployed (e.g., on Vercel) capable of sending bulk SMS/MMS campaigns via Plivo, with background job processing for reliability.
1. Setting Up the Project
Let's initialize our Next.js project and configure the necessary tools.
1.1. Create Next.js Application
Open your terminal and run the following command, replacing
plivo-marketing-app
with your desired project name. Follow the prompts (we recommend using TypeScript and Tailwind CSS).1.2. Install Dependencies
We need the Plivo SDK, Prisma, BullMQ, and other utilities.
plivo-node
: Plivo's official Node.js SDK.prisma
,@prisma/client
: Prisma CLI and client.bullmq
: Job queue system.ioredis
: Recommended Redis client for BullMQ.zod
: Schema validation library (highly recommended for API inputs).date-fns
: For reliable date/time manipulation (scheduling).-D
flags install development-only dependencies.1.3. Initialize Prisma
Set up Prisma for database management.
This creates a
prisma
directory with aschema.prisma
file and a.env
file for your database connection string.1.4. Configure Environment Variables
Open the
.env
file created by Prisma (or create.env.local
which Next.js prefers and is ignored by Git by default). Add your Plivo credentials and database/Redis URLs.Important: The placeholder values below (like
YOUR_PLIVO_AUTH_ID
) must be replaced with your actual credentials and configuration details from Plivo, your database, and Redis.PLIVO_AUTH_ID
/PLIVO_AUTH_TOKEN
: Find these on your Plivo console dashboard. Treat the Auth Token like a password – keep it secret.PLIVO_SENDER_ID
: The Plivo phone number (in E.164 format, e.g.,+14155551212
) or approved Alphanumeric Sender ID you'll send messages from. For US/Canada, this must be a Plivo number.DATABASE_URL
: Connection string for your PostgreSQL database. The example assumes a local Docker setup.REDIS_URL
: Connection string for your Redis instance.NEXT_PUBLIC_APP_URL
: The base URL where your app is accessible. Used for constructing callback URLs if needed later. Important: Update this for your production deployment.API_SECRET_KEY
: A simple secret for basic protection of internal API routes. Generate a strong random string.SECURITY NOTE: Never commit your
.env
or.env.local
file containing secrets to Git. Ensure.env.local
is in your.gitignore
file (Create Next App usually adds it).1.5. Set Up Local Database and Redis (Docker Recommended)
The easiest way to run PostgreSQL and Redis locally is using Docker. Create a
docker-compose.yml
file in your project root:Run these services:
Now your database and Redis should be running locally, accessible via the URLs specified in
.env.local
.1.6. Project Structure Overview
Your project might look something like this:
2. Implementing Core Functionality
Now, let's build the main features: defining the database structure, setting up the Plivo client, creating the job queue, and building the API endpoint for campaign creation.
2.1. Define Database Schema
Open
prisma/schema.prisma
and define models for Campaigns, Recipients, and Message Logs.mediaUrls
allows for MMS.@@unique
constraint for[campaignId, phoneNumber]
.OPTED_OUT
status.2.2. Apply Database Migrations
Generate and apply the SQL migration based on your schema changes.
This command:
prisma/migrations/
.@prisma/client
).2.3. Initialize Prisma Client
Create a reusable Prisma client instance.
PrismaClient
instances during development hot-reloading in Next.js.2.4. Initialize Plivo Client
Create a helper function to initialize the Plivo client using TypeScript.
import
, type annotations,export
).2.5. Set Up BullMQ Job Queue
Define the queue and the connection.
import()
for worker dependencies (plivoClient
,prisma
).MessageCreateParams
type is used.messageUuid
being undefined or empty array from Plivo response.2.6. Create the Campaign API Endpoint
This Next.js API route will handle campaign creation requests.
OptOut
table before creating recipients and queuing jobs.skippedCount
.3. Building the API Layer
The previous section established our primary API endpoint (
/api/campaigns
). Let's refine authentication and documentation.3.1. Authentication/Authorization
The current implementation uses a simple shared secret (
API_SECRET_KEY
) passed in the request body. This offers basic protection suitable for internal use or simple scenarios where the caller is trusted.Best Practice: For production applications, especially those exposed externally, it's strongly recommended to use more robust methods:
Authorization: Bearer <YOUR_API_KEY>
orX-API-Key: <YOUR_API_KEY>
. This is more conventional than sending secrets in the request body. Validate the key against stored (ideally hashed) keys in your database.3.2. Request Validation
We are using
zod
within the API route (pages/api/campaigns.ts
) for strict validation of the request body's structure, types, and formats (including E.164 phone numbers and URLs). This is a crucial security measure against invalid data and potential injection attacks.3.3. API Endpoint Documentation
Clear documentation is essential for API consumers.
Example Documentation (Markdown):
Parameters:
name
(string, required): Name of the campaign.message
(string, required): The text content of the message. Use\n
for line breaks.recipients
(array[string], required): List of recipient phone numbers in E.164 format. Duplicates are removed, invalid formats ignored, and numbers present in theOptOut
table are skipped.mediaUrls
(array[string], optional): An array of URLs pointing to media files for MMS messages.scheduledAt
(string, optional): An ISO 8601 formatted date-time string (UTC recommended) to schedule the campaign for future sending. If omitted, the campaign is queued immediately.apiKey
(string, required): Your secret API key (from.env.local
). Note: Using a header likeAuthorization: Bearer <key>
orX-API-Key: <key>
is preferred in production environments.Success Response: (
201 Created
)Error Responses:
400 Bad Request
: Invalid input data (validation errors, invalid schedule date, no valid recipients). Includes anerrors
object or detailedmessage
.401 Unauthorized
/403 Forbidden
: (If using header-based auth) Invalid or missing API key.405 Method Not Allowed
: Used a method other than POST.500 Internal Server Error
: Database error, queue error, missing server configuration (likePLIVO_SENDER_ID
), or other unexpected server-side issue.