Frequently Asked Questions
You can send bulk SMS messages by creating a RedwoodJS application that integrates with AWS SNS. This involves setting up a GraphQL API endpoint connected to a service function that interacts with the AWS SDK. This function handles sending messages in batches to contacts stored in your database.
RedwoodJS is the core framework for building the bulk SMS application. It provides structure, routing, GraphQL API capabilities, and integration with Prisma ORM for database interactions. This simplifies development and provides a cohesive full-stack experience.
AWS SNS is chosen for its scalability and reliability in handling message delivery. It's a fully managed service, meaning AWS handles the infrastructure, allowing developers to focus on application logic. SNS also provides direct SMS sending capabilities.
First, create a new RedwoodJS project using yarn create redwood-app. Then, install the necessary AWS SDK modules and configure your database connection in schema.prisma. Finally, set up authentication using Redwood's built-in dbAuth and define a GraphQL mutation to trigger the SMS sending process.
Prisma acts as the Object-Relational Mapper (ORM) allowing RedwoodJS to interact with the database. It simplifies database operations by providing a type-safe and easy-to-use API for querying and managing data, such as fetching contact information.
The PublishBatch API is ideal for sending messages to multiple recipients simultaneously. This optimizes the sending process and is more efficient than individual API calls for each message. However, keep in mind that SNS limits batch sizes to 10 recipients.
Yes, Prisma supports other databases besides PostgreSQL. You can configure the database connection by editing the provider field in your schema.prisma file and updating the DATABASE_URL environment variable accordingly.
Store your AWS credentials (Access Key ID and Secret Access Key) as environment variables in a .env file. RedwoodJS loads these variables automatically in development. Never hardcode credentials directly in your code.
The maximum batch size for the PublishBatch API in AWS SNS is 10. The provided code enforces this with a MAX_BATCH_SIZE constant. Exceeding this limit will cause errors.
The example code provides error handling at multiple levels: input validation for empty messages, individual message failures within a batch using the SNS response's Failed array, batch-level error handling with retries, and overall process errors using try-catch blocks. Ensure logging is configured for monitoring.
The recommended format is E.164, which includes a plus sign (+) followed by the country code, area code, and local number. The code includes a basic regular expression, and Section 8 in the original article was meant to have better validation. For robust validation, consider a dedicated library like libphonenumber-js.
Using dedicated IAM users with limited permissions is crucial for security. It reduces the potential damage from compromised credentials and ensures least privilege access. In production, create custom IAM policies to restrict access only to essential SNS actions (like Publish and PublishBatch).
The dbAuth setup protects the bulk SMS sending functionality. It ensures that only authorized users can access and trigger the GraphQL mutation responsible for sending messages, preventing unauthorized access.
While the code provides a basic regular expression for E.164 validation, consider using a specialized library like libphonenumber-js to enhance this process. A library handles various number formats and provides more accurate validation than regex alone.
Centralizing SNS client initialization in a separate library (snsClient.js) improves code organization and modularity. It makes it easier to manage and update the SNS client configuration across your application.
This guide provides a complete walkthrough for building a production-ready bulk SMS broadcasting system using the RedwoodJS framework and Amazon Simple Notification Service (AWS SNS).
We'll build an application that enables authenticated users to send a single message to a list of contacts stored in a database. This solves the common need for businesses to efficiently distribute notifications, alerts, or marketing messages via SMS.
Technologies Used:
System Architecture:
Final Outcome:
By the end of this guide, you will have a RedwoodJS application with:
Prerequisites:
1. Setting up the RedwoodJS Project
Let's initialize a new RedwoodJS project and install necessary dependencies.
Create RedwoodJS App: Open your terminal and run the following command. Choose TypeScript when prompted.
Navigate to Project Directory:
Install AWS SDK v3 for SNS: We need the specific client package for SNS.
Why
@aws-sdk/client-sns? The AWS SDK v3 is modular. Installing only the SNS client keeps our dependency footprint smaller compared to installing the entire SDK.Setup Database: RedwoodJS uses Prisma. By default, it's configured for SQLite. Let's switch to PostgreSQL (adjust connection string for your setup).
Edit
api/db/schema.prisma: Change theproviderin thedatasourceblock.Create a
.envfile in the project root (if it doesn't exist) and add your PostgreSQL connection string:Explanation: The
schema.prismafile defines our database connection and data models. The.envfile stores sensitive information like database credentials, keeping them out of version control.Initial Database Migration: Apply the initial schema setup to your database.
Your basic RedwoodJS project structure is now ready. The
apidirectory contains backend code (GraphQL API, services, database schema), and thewebdirectory contains frontend code (React components, pages).2. Implementing Core Functionality (Backend Service & SNS Client)
We'll create a reusable SNS client and a service function to handle the logic of sending bulk messages.
Configure AWS Credentials Securely: The AWS SDK needs credentials to interact with your account. We'll use environment variables, which RedwoodJS automatically loads from the
.envfile in development. Never hardcode credentials in your source code.Add the following to your
.envfile (obtain these from your AWS IAM user setup - see Section 4):Explanation: The SDK automatically detects
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, andAWS_REGION(orAWS_DEFAULT_REGION) environment variables. Storing them here keeps them secure and environment-specific.Create an SNS Client Library: This centralizes the SNS client initialization.
Create a new file:
api/src/lib/snsClient.jsExplanation: We import the
SNSClient, instantiate it (optionally passing the region), and export it. The Redwood logger is used for basic initialization feedback.Define the Contact Data Model: We need a way to store the phone numbers we want to message.
Edit
api/db/schema.prismaand add theContactmodel:Explanation: Defines a simple
Contacttable with an ID, an optional name, a unique phone number, and timestamps. Using@uniqueonphoneprevents duplicate entries.Apply Database Migration: Create and apply a migration for the new
Contactmodel.Generate Contact Scaffolding (Optional but helpful): Redwood can generate basic CRUD operations (Create, Read, Update, Delete) for the
Contactmodel. This gives us a service and SDL definitions automatically.api/src/graphql/contacts.sdl.js(GraphQL schema definitions)api/src/services/contacts/contacts.js(Service functions for DB interaction)web/src(we won't focus on the UI here, but they are generated)Create the Messaging Service: This service will contain the core logic for fetching contacts and sending the bulk SMS via SNS.
Generate a new service:
Edit
api/src/services/messaging/messaging.js:Explanation:
PublishBatchCommandfrom AWS SDK,dbfor database access,logger, oursnsClient, andUserInputErrorfor GraphQL validation errors.MAX_BATCH_SIZE(SNS limit is 10 forPublishBatch).sendBulkSmsfunction takes themessagecontent as input.idandphone).E164_REGEXbefore adding a number to a batch. Invalid numbers are skipped and logged. (Reminds user about Section 8 for better validation).PublishBatchRequestEntriesfor each valid contact in the batch, ensuring a uniqueIdfor each entry.PublishBatchCommandusingsnsClient.send().SuccessfulandFailedarrays from the SNS response, logging details about failures and updating counters. It attempts to map failure IDs back to phone numbers for better reporting.try...catchblocks for both individual batch sends and the overall process.requireAuth()is added (we'll set up auth next).3. Building the API Layer (GraphQL Mutation)
We need a way for the frontend (or external clients) to trigger the
sendBulkSmsservice function. We'll use a GraphQL mutation.Define the GraphQL Mutation:
Edit
api/src/graphql/messaging.sdl.js:Explanation:
SendBulkSmsFailuretype to structure failure details.SendBulkSmsResponsetype to structure the return value of our mutation, including success/failure counts and the detailed failures list.sendBulkSmsmutation within theMutationtype. It accepts a requiredmessagestring.@requireAuthdirective to ensure only authenticated users can call this mutation.Implement Basic Authentication (dbAuth): Redwood makes setting up simple database authentication straightforward.
Run the setup command:
This command:
User,UserCredential,UserRole) toschema.prisma.api/src/services/users) and GraphQL definitions (api/src/graphql/users.sdl.js).api/src/lib/auth.js).web/src/pages/LoginPage,web/src/pages/SignupPage, etc.) and updates routing.Apply Auth Migrations: Run the migration to add the new auth tables to your database.
Create a Test User: You'll need a user to test the authenticated mutation. You can either:
yarn rw dev) and navigate to the signup page (/signup) in your browser.yarn rw prisma studioto add a user directly to the database (remember to hash the password correctly if adding manually, or use the signup page).Link SDL to Service: Redwood automatically maps the
sendBulkSmsmutation inmessaging.sdl.jsto thesendBulkSmsfunction exported fromapi/src/services/messaging/messaging.jsbecause the names match. No extra resolver code is needed here.Testing the Mutation (GraphQL Playground):
Start the Redwood development server:
Open your browser to
http://localhost:8911/graphql.Authentication Header: You need to simulate being logged in.
/login).auth-providerheader (should bedbAuth).authorizationheader (should beBearer <session-token>).Execute the Mutation: Run the following mutation in the Playground:
Check the response in the Playground and the logs in your terminal (
yarn rw devoutput) to see the outcome. You should see logs indicating contacts being fetched, batches being sent, and the final result.4. Integrating with AWS SNS (IAM & Setup)
Properly configuring AWS permissions is crucial for security and functionality.
Create an IAM User for Your Application: It's best practice to create a dedicated IAM user with the minimum necessary permissions rather than using your root AWS account credentials.
redwood-sns-app-user).AmazonSNSFullAccesspolicy. Note: For production, create a custom policy granting onlysns:Publishandsns:PublishBatchpermissions, potentially restricted to specific regions or topics if applicable.AmazonSNSFullAccessis simpler for this guide but too permissive for production.Generate Access Keys:
redwood-sns-bulk-sms-key).Add Credentials to
.env:Open your project's
.envfile.Paste the copied keys into the respective variables:
Restart Development Server: If your dev server (
yarn rw dev) is running, stop it (Ctrl+C) and restart it to ensure it picks up the new environment variables from.env.Your application is now configured to authenticate with AWS SNS using the dedicated IAM user's credentials.
5. Implementing Error Handling, Logging, and Retries
Robust error handling and logging are essential for production systems.
Error Handling (Already Implemented):
sendBulkSms) already includes checks for an empty message (UserInputError) and basic E.164 phone number format validation.Failedarray in thePublishBatchresponse, logs each specific failure, and includes details in the mutation response.try...catchblock insendBulkSmscatches unexpected errors during the process (e.g., database connection issues, SDK client errors).@requireAuthdirective handles unauthorized access attempts.Logging (Already Implemented):
api/src/lib/logger.js) is used throughout thesendBulkSmsservice.api/src/lib/logger.js. For production, you'd typically set the level toinfoorwarnand potentially log to a file or a log aggregation service.Retry Mechanisms:
SNS Internal Retries: AWS SNS itself has built-in retry mechanisms for transient delivery issues when sending SMS messages. You generally don't need to implement complex retry logic for the final delivery attempt from SNS to the carrier.
Application-Level Retries (for
PublishBatch): ThePublishBatchAPI call itself might fail due to transient network issues or AWS throttling. Our current code logs batch errors but doesn't automatically retry the entire batch.Implementing Retries (Optional Enhancement): To add retries for failed
PublishBatchcalls, you could wrap thesnsClient.send(command)call within a loop with exponential backoff:Consideration: Adding retries increases complexity. Evaluate if the potential benefits outweigh the added code, especially given SNS's own reliability. For critical messages, retrying might be necessary. Also consider idempotency if retrying.
6. Creating Database Schema and Data Layer
This was largely covered during the setup and core functionality implementation.
Schema Definition (
api/db/schema.prisma): We defined theContactmodel and thedbAuthmodels.Data Access Layer:
dbobject imported fromsrc/lib/dbprovides typed access to your database models.messaging.js:await db.contact.findMany(...)contactsservice (api/src/services/contacts/contacts.js) generated by the scaffold command provides standard CRUD operations.Migrations:
yarn rw prisma migrate devto apply schema changes. This command:schema.prismafile to the database state.api/db/migrations.yarn rw prisma migrate deploy.Sample Data Population (Seeding): To easily add test contacts, you can use Prisma's seeding feature.
Create a seed file:
api/db/seed.jsRun the seed command (this automatically runs after
migrate devor can be run manually):7. Adding Security Features
Security is paramount, especially when dealing with potentially sensitive contact information and incurring costs via AWS services.