Frequently Asked Questions
dbAuth provides a built-in authentication system integrated with a database. It simplifies user signup, login, logout, and session management within your RedwoodJS application.
Use RedwoodJS's GraphQL API to create reminders with a scheduled time, which are then processed by a background job using node-cron or an external scheduler in production. The job queries for due reminders and sends them via the Plivo SMS API.
Plivo is a communications platform that handles sending the actual SMS messages. The RedwoodJS app interacts with Plivo's API to dispatch scheduled reminders as SMS.
The scheduler uses UTC to accurately compare the scheduledAt timestamp (stored in UTC in the database) with the server's current time, ensuring reminders are sent at the correct moment regardless of server location.
An external scheduler (like Vercel Cron Jobs or AWS EventBridge Scheduler) is strongly recommended for production serverless deployments because node-cron isn't suitable for ephemeral serverless functions.
Yes, Prisma supports various databases (MySQL, SQLite, etc.). Ensure the DATABASE_URL in your .env file is correctly configured for the chosen database.
Create a RedwoodJS app, set up a PostgreSQL database (e.g., using Docker), install necessary libraries (node-cron, plivo), configure environment variables (database, Plivo), and implement the reminder model and services.
Prisma is an ORM that simplifies database interactions. It's used to model the reminder data, manage database migrations, and provide type-safe access to the database within the RedwoodJS services.
Use the createReminder GraphQL mutation, providing the message, recipient phone number, and scheduled time in UTC. The app will handle storing and processing the reminder.
Reminders have three statuses: PENDING (waiting to be sent), SENT (successfully delivered), and FAILED (an error occurred during sending).
node-cron depends on a continuously running process, which doesn't align with the ephemeral nature of serverless functions. Serverless environments require an external scheduler to trigger the reminder processing logic.
Use Redwood's requireAuth function and filter database queries by the current user's ID to ensure users can only access and manage their own reminders.
The @@index attribute creates database indexes to optimize query performance. In the reminder schema, indexes on status and scheduledAt, and userId improve the efficiency of fetching due reminders and reminders by user.
After setting up the project, run yarn rw dev to start the development server. The frontend will be accessible on port 8910, the API on 8911, and the GraphQL Playground on 8911/graphql.
This guide provides a comprehensive walkthrough for building a full-stack application using RedwoodJS that enables users to schedule SMS reminders sent via Plivo. We'll cover everything from project setup and core logic implementation to deployment and best practices for a production-ready system.
By the end of this tutorial, you will have a functional web application where authenticated users can create, view, and delete reminders. A background process will periodically check for due reminders and use the Plivo API to dispatch them as SMS messages. This guide focuses heavily on the backend implementation details necessary for the scheduling and notification functionality.
Project Overview and Goals
What We're Building:
A multi-user web application where individuals can schedule SMS messages (reminders) to be sent to a specified phone number at a future date and time.
Problem Solved:
Automates the process of sending timely reminders via SMS, useful for appointments, tasks, events, or any time-sensitive notification. It provides a persistent, reliable mechanism managed through a web interface.
Technologies Used:
node-cron
: A simple cron-like job scheduler for Node.js. We'll use this for demonstrating the scheduling mechanism within the Node.js process. Note: For production serverless deployments, an external scheduler (like Vercel Cron Jobs, AWS EventBridge Scheduler) is strongly recommended.System Architecture:
(Note: Consider using a graphical diagram (SVG/PNG) for clearer presentation if your platform supports it.)
node-cron
within the API, ideally an external trigger in production) runs periodically.Prerequisites:
nvm
to manage Node versions.Final Outcome:
A secure, authenticated RedwoodJS application enabling users to schedule and receive SMS reminders via Plivo, with a clear path to production deployment.
1. Setting up the Project
Let's initialize our RedwoodJS application and configure the essential tools.
Create the RedwoodJS App: Open your terminal and run the
create redwood-app
command. We'll use TypeScript for enhanced type safety.yes
)Initial commit
)yes
)Navigate to Project Directory:
Verify Node and Yarn Versions: Ensure your Node.js and Yarn versions meet the prerequisites mentioned earlier.
Set up Local PostgreSQL Database (using Docker): If you don't have PostgreSQL running, Docker is a convenient way to start a local instance.
-d
), mapping port 5432 on your host to the container's port 5432. It sets the defaultpostgres
user's password. We then usedocker exec
to run thepsql
command-line tool inside the container and execute theCREATE DATABASE
command to set up theplivo_scheduler
database.Configure Environment Variables: RedwoodJS uses a
.env
file for environment variables. Create this file in the project root. Never commit.env
files to Git. Add your database connection string.DATABASE_URL
tells Prisma how to connect to your database. Adjust the username, password, host, port, and database name if they differ from the Docker example. The?schema=public
part is often required. Quotes around the value are typically not needed unless the value contains special characters problematic for the shell.Initialize Prisma: Apply the initial database schema (which is currently empty besides Prisma's internal tracking).
schema.prisma
file with the database, generates SQL migration files inapi/db/migrations
, and applies them to your database. The--name
flag provides a descriptive name for the migration.Start the Development Server: Verify the basic setup works.
http://localhost:8910
.http://localhost:8911
.http://localhost:8911/graphql
.Keep this running in a separate terminal tab during development.
2. Implementing Core Functionality (Scheduling Logic)
We'll store reminders in the database and use
node-cron
to check periodically for reminders that need sending.Install
node-cron
: Add thenode-cron
library to the API workspace.Define the Reminder Database Model: Edit the Prisma schema file (
api/db/schema.prisma
) to define the structure for storing reminders.Reminder
model stores the message content, recipient number, scheduled time (scheduledAt
should always be stored in UTC), current status, and a link (userId
) to the user who created it.status
: Tracks the lifecycle ('PENDING', 'SENT', 'FAILED').userId
/user
: Creates a one-to-many relationship betweenUser
andReminder
.@@index([status, scheduledAt])
: Crucial index for scheduler performance. Allows the database to efficiently find rows matchingstatus = 'PENDING'
andscheduledAt <= now()
.@@index([userId])
: Supports efficient querying for reminders belonging to a specific user (e.g._ in the API layer).User
model: Includes basic fields needed for authentication later (using Redwood'sdbAuth
).Apply Database Migrations: Generate and apply the migration for the new models.
Create the Scheduler Service: This service (
api/src/lib/scheduler.ts
) will contain the logic to find and process due reminders.node-cron
, Prisma (db
), logger, and thesendSms
function (to be created).SCHEDULE
(defaulting to every minute). Configurable via.env
.isJobRunning
flag as a lock to prevent the job from running concurrently if a previous run takes too long.cron.schedule
sets up the job.PENDING
reminders wherescheduledAt
(UTC) is less than or equal to the current time (now
). It's crucial the server runs in UTC sonew Date()
reflects UTC, ensuring accurate comparison.take
to process reminders in batches (configurable viaSCHEDULER_BATCH_SIZE
).sendSms
.SENT
on success orFAILED
on error usingdb.reminder.update
.try...catch
.finally
block ensures theisJobRunning
lock is always released.Start the Scheduler: The scheduler needs to be initialized when the API server starts. A common place in Redwood is within the server configuration or alongside the db client initialization, ensuring it runs once. Let's modify the db client setup file (
api/src/lib/db.ts
)../scheduler
file after thedb
client is potentially initialized. The dynamicimport()
ensures it runs.process.env.NODE_ENV !== 'test'
,process.env.DISABLE_SCHEDULER !== 'true'
) to prevent the scheduler from running during tests or if explicitly disabled via an environment variable.node-cron
initialization method is unsuitable for serverless and directs the user to the Deployment section for the correct approach using external triggers.3. Building the API Layer (GraphQL)
We'll use Redwood's generators to create the GraphQL schema definition (SDL) and service implementations for managing reminders.
Set up Authentication: Reminders should be user-specific. We'll use Redwood's built-in
dbAuth
.hashedPassword
,salt
, etc.) to theUser
model inschema.prisma
(we already added them, but it ensures they're standard).api/src/functions/auth.ts
).api/src/services/auth.ts
).web/src/pages/LoginPage
,web/src/pages/SignupPage
, etc.).AuthContext
(web/src/auth.ts
).yarn rw setup auth dbAuth
, if you hadn't already manually added the necessary fields (hashedPassword
,salt
, etc.) to yourUser
model inschema.prisma
(as we did in our example), you must run a database migration to add them:Generate SDL and Services for Reminder:
Reminder
model inschema.prisma
and generates:api/src/graphql/reminders.sdl.ts
: Defines the GraphQL schema types (Reminder
,CreateReminderInput
,UpdateReminderInput
) and CRUD operations (queries likereminders
,reminder
, and mutations likecreateReminder
,updateReminder
,deleteReminder
).api/src/services/reminders/reminders.ts
: Implements the resolver functions for the generated SDL operations, interacting with the database via Prisma.api/src/services/reminders/reminders.scenarios.ts
: Seed data for testing.api/src/services/reminders/reminders.test.ts
: Basic test structure.Secure the Reminder Service: Modify the generated service file (
api/src/services/reminders/reminders.ts
) to ensure users can only access and manage their own reminders. Use Redwood'srequireAuth
utility and filter queries/mutations by the current user's ID.requireAuth()
: This function (provided byyarn rw setup auth dbAuth
) checks if a user is logged in (context.currentUser
is set). If not, it throws anAuthenticationError
.where: { userId: context.currentUser?.id }
: This clause is added tofindMany
,findFirst
,update
, anddelete
operations (or checks preceding them) to ensure actions only affect reminders belonging to the currently authenticated user.RedwoodGraphQLError
: Used for user-facing errors like ""Reminder not found.""updateReminder
to prevent updating non-pending reminders (example of business logic).Testing API Endpoints (Example using GraphQL Playground):
Start the dev server:
yarn rw dev
Sign up and log in through the frontend UI (e.g.,
http://localhost:8910/signup
,http://localhost:8910/login
).Open the GraphQL Playground:
http://localhost:8911/graphql
The Playground usually handles authentication cookies automatically after you log in via the frontend on the same
localhost
domain. If not, you might need to manually copy authentication headers/tokens.Create Reminder Mutation:
Variables:
(Replace phone number and use a future UTC date/time)
Expected Response (JSON):
Get Reminders Query:
Expected Response (JSON):
Test
updateReminder
anddeleteReminder
similarly, ensuring you use the ID of a reminder created by the logged-in user.4. Integrating with Plivo
Now, let's connect our application to Plivo to actually send the SMS messages.
Obtain Plivo Credentials and Phone Number:
Store Credentials Securely: Add your Plivo credentials and sender number to the
.env
file. Do not commit this file.PLIVO_SENDER_NUMBER
must be in E.164 format (e.g.,+15551234567
). We also addedCRON_TRIGGER_SECRET
for later use in serverless deployments.Install Plivo Node.js SDK: Add the official Plivo SDK to the API workspace.
Create Plivo Client Utility: Create a file (
api/src/lib/plivoClient.ts
) to initialize and export the Plivo client instance.