Frequently Asked Questions
You can send bulk SMS messages by creating a RedwoodJS service that interacts with the Plivo API. This service will handle formatting destination numbers and sending the message text via a GraphQL mutation secured with `@requireAuth`.
Plivo is a cloud communications platform that provides SMS, voice, and WhatsApp APIs, used in this RedwoodJS application for its robust messaging capabilities and support for bulk sending. It allows you to send messages to multiple recipients with a single API request, reducing latency and simplifying logic.
RedwoodJS offers an integrated full-stack structure, uses GraphQL, and provides developer-friendly features like generators and cells. Its backend services neatly encapsulate business logic like Plivo integration, offering a streamlined development experience for building a bulk SMS feature.
The architecture involves a web frontend sending requests to a RedwoodJS GraphQL API. The API interacts with a RedwoodJS Plivo service, which formats and sends messages via the Plivo API. Optionally, a RedwoodJS database logs messages and handles user interactions.
Create a new RedwoodJS project, install the Plivo Node.js SDK and types in the API workspace, and configure environment variables (`PLIVO_AUTH_ID`, `PLIVO_AUTH_TOKEN`, `PLIVO_SOURCE_NUMBER`) in a `.env` file. These credentials are essential for interacting with the Plivo API.
Your Plivo Auth ID and Auth Token can be found on the main dashboard page after logging in to your Plivo Console at https://console.plivo.com/. Plivo documentation provides visual guides to their location on the dashboard if needed.
Your Plivo phone number can be found in the Plivo Console by navigating to Messaging -> Phone Numbers -> Your Numbers. Be sure to select a number enabled for SMS and compliant with country regulations.
Plivo expects destination phone numbers to be concatenated into a single string delimited by the '<' character. For instance, "14155551212<14155551313<14155551414". This allows sending to multiple recipients in one API request.
Secure the endpoint using `@requireAuth` directive in the GraphQL schema and set up an authentication provider (like dbAuth, Auth0, Netlify Identity, etc.). Restrict access further using roles (e.g., "admin", "manager") if needed.
While Plivo handles some number variations, validate and normalize numbers into E.164 format (e.g., +14155551212) before sending them to the service to prevent errors. Libraries like `libphonenumber-js` are helpful for this purpose.
The `sendBulkSms` service function uses try-catch blocks to capture and log errors during Plivo API calls. The function returns a structured response indicating success or failure with error messages. For production, consider more robust retry mechanisms or queueing systems.
Utilize Redwood's built-in Pino logger (`src/lib/logger.ts`) to log successful operations (info), failures (error), and detailed information (debug). Configure log levels per environment and consider forwarding logs to dedicated logging services in production.
Initializing the Plivo client outside the `sendBulkSms` function allows for potential reuse and avoids recreating the client instance on every call, improving efficiency. It also ensures that environment variables needed for the Plivo client are properly loaded.
Background jobs are recommended for production-level bulk SMS sending, especially for large broadcasts or potential Plivo delays. This decouples sending from the initial request and improves reliability by handling retries and failures independently.
Yes, the example provides an optional logging mechanism using Prisma. A `BulkMessageJob` model is added to the Prisma schema, allowing you to store job status, recipient count, messages, errors, and potentially destinations for tracking and analysis.
This guide provides a comprehensive walkthrough for building a production-ready bulk SMS broadcasting feature within a RedwoodJS application using the Plivo messaging API. We'll cover everything from initial project setup to deployment and monitoring, enabling you to reliably send messages to multiple recipients with a single API request.
By the end of this tutorial, you will have a RedwoodJS application with a secure API endpoint capable of accepting a list of phone numbers and a message text, then using Plivo to dispatch these messages efficiently.
Project Overview and Goals
What We're Building:
We are building a backend service within a RedwoodJS application that exposes a GraphQL mutation. This mutation will accept an array of destination phone numbers and a message body. It will then interact with the Plivo API to send the message to all specified recipients simultaneously using Plivo's bulk messaging feature.
Problem Solved:
This solves the need to efficiently send the same SMS message to many recipients without iterating and making individual API calls for each number. This reduces latency, simplifies application logic, and leverages Plivo's infrastructure optimized for bulk delivery.
Technologies Used:
System Architecture:
Prerequisites:
Final Outcome:
A functional RedwoodJS backend service with a testable GraphQL endpoint for sending bulk SMS messages via Plivo.
1. Setting up the Project
Let's start by creating a new RedwoodJS project and installing the necessary dependencies.
Create RedwoodJS App: Open your terminal and run:
Follow the prompts. We recommend choosing TypeScript.
Navigate to Project Directory:
Install Plivo Node.js SDK: The Plivo SDK will interact with their API. Install it specifically in the
api
workspace:Configure Environment Variables: Sensitive credentials like API keys should never be hardcoded. RedwoodJS uses
.env
files.Create a
.env
file in the project root:touch .env
Add the following variables, replacing the placeholder values:
How to find these values:
PLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
: Find these on the main dashboard page. See Plivo documentation for visual examples:PLIVO_SOURCE_NUMBER
: Navigate toMessaging
->Phone Numbers
->Your Numbers
. Copy a number that is SMS-enabled and suitable for sending broadcasts (check country regulations). See Plivo documentation for visual examples:Add
.env
to.gitignore
: Ensure your.env
file (containing secrets) is listed in your root.gitignore
file. RedwoodJS usually adds this by default, but double-check.Purpose of Configuration: Using environment variables keeps your credentials secure and separate from your codebase, making it easy to manage different credentials for development, staging, and production environments.
2. Implementing Core Functionality (Plivo Service)
RedwoodJS services encapsulate business logic on the API side. We'll create a service to handle interactions with the Plivo API.
Generate Messaging Service: Use the RedwoodJS CLI generator:
This creates
api/src/services/messaging/messaging.ts
and related test files.Implement the Bulk Send Logic: Open
api/src/services/messaging/messaging.ts
and replace its contents with the following:Explanation:
sendBulkSms
Function:destinations
(array of strings) andtext
(string).<
character (e.g._`""14155551212<14155551313<14155551414""`
). We usedestinations.join('<')
.plivoClient.messages.create
with the source number_ the formatted destination string_ and the message text.logger
for informational and error messages.try...catch
block wraps the API call. If an error occurs_ it's logged_ and asuccess: false
response is returned.Design Pattern: This uses a simple Service Layer pattern_ isolating the Plivo interaction logic from the GraphQL resolver layer.
Alternative Approach: Instead of using the bulk
dst
format_ you could loop through the destinations and make individualmessages.create
calls (perhaps concurrently usingPromise.all
). However_ this is less efficient_ potentially hits API rate limits faster_ and doesn't leverage Plivo's dedicated bulk feature. For true bulk sending_ the<
delimited string is the intended method.3. Building the API Layer (GraphQL)
RedwoodJS uses GraphQL for its API. We need to define a mutation to expose our
sendBulkSms
service function.Define GraphQL Schema: Open or create
api/src/graphql/messaging.sdl.ts
and add the following:Explanation:
BulkSmsResponse
Type: Defines the structure of the data returned by the mutation. We keep it simple_ mirroring our service response.Mutation
Type: Defines thesendBulkMessage
mutation.destinations
(an array of non-null strings) andtext
(a non-null string). GraphQL enforces these types.BulkSmsResponse
type (non-null).@requireAuth
: This is a RedwoodJS directive that ensures only authenticated users can call this mutation. We'll set up authentication later (Section 7)_ but it's crucial to include this now. You can add role-based access control too.Resolver Mapping (Automatic): RedwoodJS automatically maps the
sendBulkMessage
mutation defined in the SDL to thesendBulkSms
function exported fromapi/src/services/messaging/messaging.ts
. No explicit resolver code is needed inapi/src/functions/graphql.ts
for this mapping.Testing the Endpoint: Once your RedwoodJS app is running (
yarn rw dev
)_ you can test this mutation using a GraphQL client like Apollo Studio Sandbox (usually available athttp://localhost:8911/graphql
) orcurl
.Curl Example: (Requires authentication setup first - see Section 7. For initial testing without auth_ temporarily remove
@requireAuth
)Note: Complex JSON payloads with nested quotes in
--data-raw
can be tricky to escape correctly across different terminals. Using a dedicated GraphQL client or sending the JSON payload from a file (--data @payload.json
) is often more reliable.Expected JSON Request Body (for the
curl
example):Example Success JSON Response:
Example Error JSON Response (e.g._ invalid number):
4. Integrating with Plivo (Third-Party Service)
We've already done the core integration within the service file (
messaging.ts
). This section reiterates the key integration points and configuration details.Configuration (Recap):
PLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
are stored securely in.env
and accessed viaprocess.env
.PLIVO_SOURCE_NUMBER
is stored in.env
and used as thesrc
parameter in the API call.Secure Handling of Secrets:
.env
: Never commit this file to version control. Ensure it's in.gitignore
..env
file in your deployment bundle.Plivo SDK Initialization: The
PlivoClient
is initialized once inmessaging.ts
using the environment variables. This is the primary point of interaction with the Plivo service.Fallback Mechanisms (Considerations):
try...catch
. For production_ if Plivo is temporarily unavailable_ you might want a more robust retry mechanism (Section 5) or queueing system (like BullMQ with Redis) to attempt sending later.5. Error Handling_ Logging_ and Retry Mechanisms
Robust applications need solid error handling and logging.
Error Handling Strategy:
sendBulkSms
function usestry...catch
to capture errors during the Plivo API call. It logs the error and returns a structuredBulkSmsResponse
withsuccess: false
and an error message/detail.BulkSmsResponse
type.catch
block. Parsing theerror
object (which might vary slightly based on the SDK version) can provide more specific feedback. Refer to Plivo API documentation for error codes.Logging:
src/lib/logger.ts
).logger.info
: Used for successful operations or key events.logger.error
: Used for failures_ catching exceptions.logger.debug
: Used for detailed information (like API responses) helpful during development/troubleshooting. Can be disabled in production via log level configuration.logger.warn
: Used for potential issues (e.g._ invalid phone number formats before validation).api/src/lib/logger.ts
or via environment variables (e.g._LOG_LEVEL=info
for production_LOG_LEVEL=debug
for development).Retry Mechanisms:
catch
block (e.g._ retry 2-3 times with a short delay).async-retry
can simplify this.dbAuth
or a library like BullMQ + Redis).Example (Conceptual Retry Logic within
catch
- Conceptual example only):6. Database Schema and Data Layer (Optional Logging)
While not strictly required for sending_ logging message attempts or storing recipient lists can be useful. We'll add a simple log.
Define Prisma Schema: Open
api/db/schema.prisma
and add a model to log bulk message jobs:Apply Migrations: Generate and apply the database migration:
Follow the prompts to create the migration file and apply it to your development database.
Update Service to Log: Modify
api/src/services/messaging/messaging.ts
to interact with the database.Explanation:
db
).BulkMessageJob
record withstatus: 'PENDING'
.status: 'SUBMITTED'
and store themessageUuid
returned by Plivo (useful for correlating logs).status: 'FAILED'
and store the error message. Added a nested try-catch for the DB update on failure to prevent masking the original Plivo error if the DB update itself fails.Performance/Scale: For very high volume, writing to the DB on every request can become a bottleneck. Consider batching log writes or logging asynchronously (e.g., via background jobs). Ensure relevant columns (
status
,createdAt
) are indexed (added@@index
).7. Adding Security Features
Securing your API endpoint is critical, especially when it triggers external actions and potentially incurs costs.
Authentication:
@requireAuth
to the GraphQL mutation definition (messaging.sdl.ts
). This is the first line of defense.dbAuth
(built-in, username/password stored in your DB), or third-party providers like Auth0, Netlify Identity, Firebase Auth, etc.dbAuth
:@requireAuth
functional. Users will need to log in to obtain a session/token to call the protected mutation.Authorization (Role-Based Access Control):
dbAuth
supports roles.Input Validation and Sanitization:
destinations
is an array of strings andtext
is a string.[String!]!
means the array itself cannot be null, and its elements cannot be null.+14155551212
). While Plivo might handle some variations, it's best practice to validate/normalize numbers before sending them to the service. You can add validation logic in the service or preferably before calling the mutation (e.g., on the frontend or in a validation layer). Libraries likelibphonenumber-js
can help.