Frequently Asked Questions
Start by creating a new RedwoodJS application using the command `yarn create redwood-app ./your-project-name`. Then navigate to the project directory. Install the necessary Twilio helper library in the API workspace with `yarn workspace api add twilio`.
Twilio's Programmable Messaging API is used to send SMS reminders. Its Message Scheduling feature enables reliable delivery without building a custom scheduling system. This simplifies development and ensures robust functionality.
Scheduling is handled by the Twilio Node.js helper library and RedwoodJS services. The `scheduleReminder` function calculates the appropriate `sendAt` time, considering the appointment time and a predefined offset. It uses the user's time zone for display but schedules based on UTC for consistency.
Store appointment times in UTC in your database. The user's time zone is stored separately and is used only for displaying the appointment time in the user's local time and formatting the reminder message. Twilio scheduling always uses UTC.
You need Node.js v18 or later, Yarn, a Twilio account, a Twilio phone number with SMS capability, and a configured Twilio Messaging Service. Familiarity with JavaScript, Node.js, React, GraphQL, and database concepts is helpful.
Twilio's Message Scheduling requires a Messaging Service. It offers benefits like sender ID pools, scalability, and advanced opt-out handling, making it more suitable than sending messages from a single number directly.
While RedwoodJS defaults to SQLite for simplicity, PostgreSQL is recommended for production environments due to its robustness and scalability. You can configure the database connection string in the `.env` file.
Your Twilio Account SID, Auth Token, and Messaging Service SID should be stored as environment variables in a `.env` file in your project's root directory. RedwoodJS automatically loads these variables, making them available to your services.
The `scheduledMessageSid` field in the database stores the unique identifier of the scheduled message returned by Twilio. This allows you to cancel or modify the scheduled reminder later if needed.
The provided code includes functionality to cancel existing reminders using the stored `scheduledMessageSid`. This cancellation logic is implemented within the `updateAppointment` and `deleteAppointment` mutations.
Use the E.164 format (e.g., +15551234567) for phone numbers. This ensures international compatibility. The provided code includes validation to enforce this format.
RedwoodJS uses GraphQL for its API. The `appointments.sdl.ts` file defines the schema for appointment data, and `appointments.ts` contains resolver functions for interacting with the database and Twilio.
RedwoodJS uses Prisma as its database toolkit. You define the data model in the `schema.prisma` file and manage migrations using the Redwood CLI.
You can find your Account SID and Auth Token on the main dashboard ("Account Info") of the Twilio Console after logging in.
The example code schedules the reminder 60 minutes before the appointment. The `scheduleReminder` function handles scheduling logic, ensuring the reminder is scheduled within Twilio's constraints (15 minutes to 7 days in the future).
Yes, the SMS reminder content can be customized. The `scheduleReminder` function demonstrates formatting the message body and incorporating details like the recipient's name and the appointment time and timezone.
Build Appointment Reminders with RedwoodJS and Twilio SMS
This guide provides a step-by-step walkthrough for building a web application using RedwoodJS that enables users to create appointments and automatically receive SMS reminders via Twilio's Programmable Messaging API. We will leverage Twilio's Message Scheduling feature for robust, reliable delivery without needing a custom scheduling system.
By the end of this tutorial, you will have a functional RedwoodJS application capable of:
This guide assumes you have a basic understanding of JavaScript, Node.js, React, GraphQL, and database concepts.
Project Overview and Goals
Goal: Build a full-stack application where users can manage appointments, and the system automatically schedules and sends SMS reminders a configurable amount of time before the appointment.
Problem Solved: Automates the process of sending timely appointment reminders, reducing no-shows and improving communication. It eliminates the need for manual reminder processes or complex self-hosted scheduling infrastructure by using Twilio's native scheduling.
Technologies:
System Architecture:
User Browser interacts with Redwood Frontend, which communicates via GraphQL with the Redwood API. The API uses Prisma to talk to the Database and makes calls to the Twilio API.
Prerequisites:
1. Setting up the RedwoodJS Project
Let's start by creating a new RedwoodJS application.
Create the RedwoodJS App: Open your terminal and run the following command, replacing
appointment-reminder-app
with your desired project name:Navigate into the Project Directory:
Install Twilio Helper Library: Install the official Twilio Node.js helper library specifically in the API workspace:
Environment Variables: RedwoodJS uses a
.env
file for environment variables. Create this file in the project root:Add the following variables to your
.env
file. We will obtain these values in Step 4 (Integrating with Twilio). Do not commit this file to version control if it contains sensitive credentials. Redwood's.gitignore
includes.env
by default.TWILIO_ACCOUNT_SID
: Your primary Twilio account identifier.TWILIO_AUTH_TOKEN
: Your secret token for authenticating API requests. Treat this like a password.TWILIO_MESSAGING_SERVICE_SID
: The unique ID for the Twilio Messaging Service you'll configure. Scheduling requires a Messaging Service.DATABASE_URL
: The connection string for your database. Redwood defaults to SQLite for quick setup, but PostgreSQL is recommended for production. Update this connection string according to your database provider (e.g., Railway, Supabase, Heroku Postgres, local installation). For SQLite (default), the line would beDATABASE_URL="file:./dev.db"
.2. Creating the Database Schema and Data Layer
We'll define the structure for storing appointment information using Prisma.
Define the Prisma Schema: Open
api/db/schema.prisma
and replace its contents with the followingAppointment
model:appointmentTime
: We store the appointment time in UTC. This is crucial for consistent scheduling across time zones.timeZone
: Storing the original time zone allows us to format the time correctly for the user and potentially adjust reminder messages.scheduledMessageSid
: This optional field will hold the unique identifier (Sid
) of the scheduled message returned by Twilio. This is essential if you need to cancel or modify the reminder later.@@index([appointmentTime])
: Added an index to potentially improve query performance when sorting or filtering by appointment time.Apply Migrations: Run the following command to create and apply the database migration. When prompted, give your migration a descriptive name (e.g.,
create_appointment_model
).This command does three things:
3. Building the API Layer (GraphQL Services and SDL)
RedwoodJS uses GraphQL for its API. We'll generate the necessary SDL (Schema Definition Language) and service files to handle CRUD operations for appointments and interact with Twilio.
Generate SDL and Service: Use Redwood's generator to scaffold the GraphQL types, queries, mutations, and the service file for the
Appointment
model:This creates:
api/src/graphql/appointments.sdl.ts
: Defines the GraphQL schema (types, queries, mutations).api/src/services/appointments/appointments.ts
: Contains the business logic (resolvers) for the SDL definitions.api/src/services/appointments/appointments.scenarios.ts
: For defining seed data for testing.api/src/services/appointments/appointments.test.ts
: For writing unit tests.Refine the GraphQL Schema: Open
api/src/graphql/appointments.sdl.ts
. Redwood generates basic CRUD operations. We need to adjust the input types slightly, particularly forappointmentTime
.appointmentTime
as aDateTime
(which Apollo Server handles as an ISO 8601 string) and assume it's provided in UTC from the client.@requireAuth
is added for basic authorization (we'll touch on setup later). Remove it if you don't need authentication initially.Implement Service Logic (Including Twilio Integration): This is where the core logic resides. Open
api/src/services/appointments/appointments.ts
and implement the resolvers, including the Twilio scheduling logic within thecreateAppointment
andupdateAppointment
mutations.scheduleReminder
Helper: Encapsulates the logic for calculatingsendAt
, checking Twilio's constraints (15 min - 7 days), canceling previous messages (ifscheduledMessageSid
exists and client is available), formatting the message body with the correct time zone (with fallback), and calling the Twilio API. Now checks for client availability.appointmentTime
(assumed UTC) directly. ThetimeZone
is used only for formatting the message content for the user.+15551234567
) for international compatibility. Validation moved to a helper.try...catch
around Twilio calls and logs errors using Redwood'slogger
. Crucially, it handles potential failures during scheduling and cancellation. Added checks for Twilio client initialization.validateAppointmentInput
helper for consistency increate
andupdate
. Added timezone validation robustness withtry/catch
.updateAppointment
anddeleteAppointment
using the storedscheduledMessageSid
. It logs errors if cancellation fails but generally proceeds with the primary operation (update/delete). Now checks for client availability before attempting cancellation.4. Integrating with Twilio
Now, let's get the necessary credentials and configure the Twilio Messaging Service.
Get Twilio Account SID and Auth Token:
TWILIO_ACCOUNT_SID
andTWILIO_AUTH_TOKEN
variables in your.env
file.Purchase a Twilio Phone Number:
Create and Configure a Twilio Messaging Service:
Phone Number
. Click Continue.MG
). Copy this value.TWILIO_MESSAGING_SERVICE_SID
variable in your.env
file.Why a Messaging Service? Twilio's Message Scheduling feature requires sending via a Messaging Service. It provides features like sender ID pools, scalability, content intelligence, and advanced opt-out handling, making it superior to sending from a single number directly for applications.
Restart Redwood Dev Server: If your development server is running, stop it (
Ctrl+C
) and restart it to load the new environment variables:5. Building the Frontend (Web Side)
Now, let's create the user interface for managing appointments using Redwood's generators and React components.
Generate Pages and Cells: We need pages to list appointments and a form to create/edit them. Redwood Cells handle data fetching gracefully.
Implement
AppointmentsCell
: Openweb/src/components/AppointmentsCell/AppointmentsCell.tsx
. This component fetches and displays the list of appointments.(Implementation details for React components like
AppointmentsCell
,AppointmentForm
,AppointmentsPage
,NewAppointmentPage
,EditAppointmentPage
, andEditAppointmentCell
would follow here, including form handling, date/time input (handling time zones and converting to UTC for the API), displaying appointments, and linking between pages. This typically involves standard React/GraphQL client patterns using Redwood's helpers like<Form>
,<Submit>
,<FieldError>
,useMutation
, etc. A full implementation is beyond the scope of this Markdown rewrite task but would be the next logical step in the tutorial.)