Frequently Asked Questions
Start by creating a new RedwoodJS app using `yarn create redwood-app ./vonage-scheduler`. Then, install necessary dependencies like `@vonage/server-sdk`, `uuid`, and `date-fns` using yarn. Set up your database connection in `api/db/schema.prisma`, define the `Appointment` model within the schema, and apply the migration using `yarn rw prisma migrate dev`.
The Vonage Messages API is used to send SMS confirmations and reminders to users who book appointments through your RedwoodJS application. It's integrated using the `@vonage/server-sdk` which is initialized with your Vonage API key and secret.
RedwoodJS utilizes Prisma as its Object-Relational Mapper (ORM) for database interactions. This simplifies database access, migrations, and management within the application. Prisma also makes it easy to switch databases (PostgreSQL, SQLite, MySQL).
The article suggests sending SMS reminders a configurable amount of time before the appointment. The timing is controlled by an environment variable (`APPOINTMENT_REMINDER_MINUTES_BEFORE`) and an external scheduler triggers the reminder function.
Yes, RedwoodJS, through Prisma, supports SQLite and MySQL in addition to PostgreSQL. Adjust the provider and URL settings within your `schema.prisma` file to switch databases.
Generate a RedwoodJS page with `yarn rw g page AppointmentBooking /book` and modify the generated component to include a form with fields for selecting the appointment time and entering a phone number. The form submission should trigger the `createAppointment` mutation.
An external scheduler like Vercel Cron Jobs or OS cron is used to periodically trigger a RedwoodJS function (`/api/sendReminders`) responsible for checking upcoming appointments and sending reminder SMS messages. This function interacts with both the database and the Vonage API.
Create a helper function (`api/src/lib/vonage.js`) to initialize the Vonage client using `@vonage/server-sdk`. Then within your services, import and call this function to send SMS messages via the Vonage API. Ensure API keys and secrets are stored securely in environment variables.
The `sendSms` helper function in the article is designed to handle common Vonage API errors by returning a specific object that includes success status, any error messages and details, and an optional message ID. The service then logs these details and uses the success status to update appointment confirmation status appropriately.
The booking code provides a unique identifier for each appointment. Although not fully implemented in this example, it's intended to facilitate appointment cancellation or lookup functionalities in a more complete application.
Reminders are sent via a dedicated Redwood Function (`/api/sendReminders`) triggered externally by a scheduler. This function queries the database for upcoming appointments and sends SMS reminders using the Vonage API.
The E.164 format ensures consistent and reliable phone number handling, crucial for SMS delivery. It's the internationally recommended standard and using it avoids issues caused by varying national number formats. Libraries like `libphonenumber-js` provide robust validation.
You'll need Node.js, Yarn, a Vonage API account with a virtual number, access to a database (PostgreSQL, SQLite, or MySQL), and basic command-line familiarity. The frontend examples also assume Tailwind CSS is set up.
This guide provides a complete walkthrough for building a web application using the RedwoodJS framework that enables users to book appointments and receive SMS reminders via the Vonage Messages API. We'll cover everything from project setup and core feature implementation to deployment and troubleshooting.
By the end of this tutorial, you'll have a functional RedwoodJS application featuring:
Target Audience: Developers familiar with JavaScript and Node.js, with some exposure to React and database concepts. Prior RedwoodJS experience is helpful but not strictly required.
Technologies Used:
@vonage/server-sdk
v3+).Project Overview and Goals
We aim to build an application that solves the common problem of scheduling appointments and reducing no-shows through automated reminders.
Core Features:
System Architecture:
cron
) runs periodically to trigger a Redwood Function (/api/sendReminders
) which checks for upcoming appointments and triggers reminder SMS via the Vonage API.Prerequisites:
yarn
commands, butnpm
equivalents generally work.1. Setting Up the RedwoodJS Project
Let's initialize our RedwoodJS application and configure the basic structure.
Create RedwoodJS App: Open your terminal and run:
Follow the prompts. Choose TypeScript if you prefer, though this guide uses JavaScript.
Install Dependencies: We need the Vonage Server SDK v3+,
uuid
, anddate-fns
.Note:
node-cron
is removed as the scheduling logic relies on an external trigger for the function.Database Setup (Prisma): RedwoodJS uses Prisma for database interaction.
api/db/schema.prisma
.datasource db
block for your chosen database. For PostgreSQL:Define Database Schema: Add the
Appointment
model toapi/db/schema.prisma
:slotDateTime
: Stores the exact date and time, ideally in UTC.phoneNumber
: Stores the recipient number for SMS. E.164 format is crucial.bookingCode
: A unique identifier generated during booking.confirmed
,reminderSent
: Flags to track SMS status success.@@index
: Improves performance when searching for appointments by time.Create and Apply Migration: This command creates SQL migration files based on your schema changes and applies them to your database.
Enter a name for the migration when prompted (e.g.,
add_appointment_model
).Environment Variables (.env): RedwoodJS uses a
.env
file at the project root for environment variables. Create it if it doesn't exist and add your database connection string and Vonage credentials. Consider using.env.defaults
for non-secret default values likeAPPOINTMENT_REMINDER_MINUTES_BEFORE
.DATABASE_URL
: The connection string for your database. Ensure it matches the provider inschema.prisma
.VONAGE_API_KEY
/VONAGE_API_SECRET
: Found on your Vonage Dashboard under ""API settings"". Treat these as secrets! Do not commit them to Git.VONAGE_FROM_NUMBER
: A Vonage virtual number capable of sending SMS, purchased from the Vonage Dashboard under ""Numbers"". Ensure it's in E.164 format (e.g.,14155550100
).APPOINTMENT_REMINDER_MINUTES_BEFORE
: How long before the appointment the reminder should be sent.CRON_SECRET
(Optional but Recommended): A secret key to verify requests to thesendReminders
function if triggered via HTTP.Initialize Vonage Client (API Side): Create a utility file to initialize the Vonage client instance using the v3 SDK.
@vonage/server-sdk
v3+ syntax (new SMS(...)
,vonage.messages.send(...)
).sendSms
helper adapted for v3:SMS
class.vonage.messages.send
.{ success: boolean, ... }
for consistency within the app's logic, capturing errors from the SDK's thrown exceptions.messageUuid
instead of the oldmessage-id
.to
/from
.2. Implementing Core Functionality (Booking)
Now, let's build the GraphQL API and the service logic for creating appointments.
Generate SDL and Service: Redwood's generators scaffold the necessary files.
Define GraphQL Schema (SDL): Modify the generated
appointments.sdl.js
(or.graphql
file if preferred) to include a specific input type for creation and define thecreateAppointment
mutation.CreateAppointmentInput
: Defines the data needed from the client (web side).createAppointment
: The mutation the web side will call.@skipAuth
: For simplicity in this guide, we bypass authentication. In a real app, you'd use@requireAuth
and implement Redwood Auth (yarn rw setup auth ...
).Query
block, although not the focus here.Implement the Service Logic: Update the
createAppointment
function inapi/src/services/appointments/appointments.js
.libphonenumber-js
. Added validation forslotDateTime
format.sendSms
helper.confirmed
in the DB only ifsendSms
returnssuccess: true
. Updates the in-memorynewAppointment
object to match the DB state before returning it. Includes logging for DB update failures post-SMS success.logger
, handles potential database and SMS errors based on thesendSms
return value.3. Building the Frontend (Web Side)
Let's create a simple React page with a form to book appointments.
Generate Page:
Create the Form Component: Modify
web/src/pages/AppointmentBookingPage/AppointmentBookingPage.js
.gql
Import: Addedimport gql from 'graphql-tag'
as it's used.useMutation
Hook: UpdatedonCompleted
to check theconfirmed
status returned from the mutation and provide more specific user feedback usingtoast.warn
if confirmation failed. Added form reset logic using a changingkey
prop on theForm
.min
attribute toDatetimeLocalField
to prevent selecting past dates dynamically. Improved validation messages.datetime-local
value needs conversion to ISO string (UTC) for the backend.Run the Development Server:
Navigate to
http://localhost:8910/book
(or the port specified). You should see the form. Try booking an appointment. Check your terminal logs (api
side) and your phone for the SMS confirmation! Check the database to see theconfirmed
flag.4. Implementing Scheduled Reminders
We need a mechanism to periodically check for upcoming appointments and send reminders. We'll use a RedwoodJS Function triggered by an external scheduler (like OS cron, GitHub Actions Schedule, Vercel Cron Jobs, Render Cron Jobs, etc.). Running cron within a potentially serverless API function is unreliable.