Frequently Asked Questions
Use the `/send` endpoint of the Fastify API, specifying the campaign ID. The API integrates with Plivo's SMS platform to dispatch messages to opted-in subscribers. This setup handles subscriber management, consent, and reliable message delivery using Fastify's efficient framework and Plivo's communication platform.
Fastify is a high-performance Node.js web framework used to build the backend API service for the SMS marketing application. It was chosen for its speed, extensibility, and developer-friendly features such as built-in validation and logging, which enhance the performance and maintainability of the application.
Plivo is a cloud communications platform that offers robust SMS API capabilities. It's integrated into the project for its reliable message delivery, easy-to-use developer tools, and scalability to handle potentially large volumes of messages.
Pino-pretty is best used during development. It formats logs for improved readability in the console. For production environments, standard JSON logging is preferred for structured logging, which is easier to parse by monitoring systems.
While the tutorial uses PostgreSQL, you could potentially adapt the project to use a different database. You'd need to adjust the Prisma schema and connection string in the `.env` file and might need to modify database interactions in the service layer if there are specific syntax differences.
First, create a Plivo account and obtain your Auth ID, Auth Token, and a Plivo phone number. Then, configure these credentials in the project's `.env` file. The Plivo SDK is used to send SMS messages via their API.
Prisma acts as an Object-Relational Mapper (ORM), simplifying interactions with the PostgreSQL database. It allows developers to work with database records as JavaScript objects, abstracting away complex SQL queries, improving code readability, and type safety.
Docker is used for containerizing the Fastify application, ensuring consistent deployment across different environments. This containerization approach makes the deployment process more predictable and manageable, whether deploying to a local server, a cloud platform, or other containerized environments.
The tutorial recommends using the latest Long-Term Support (LTS) version of Node.js. LTS versions offer stability, security, and ongoing support, which are essential for production applications. Use `node -v` to check your Node.js version.
The Fastify API exposes endpoints for subscriber management. `POST /api/v1/subscribers` creates a new subscriber, `GET /api/v1/subscribers` retrieves all subscribers, `GET /api/v1/subscribers/:id` retrieves a specific subscriber by ID, `PUT /api/v1/subscribers/:id/optin` updates the opt-in status, and `DELETE /api/v1/subscribers/:id` deletes a subscriber.
The application follows a client-server architecture where a client (e.g., a web interface) interacts with the Fastify API, which in turn communicates with the Plivo API for sending SMS messages. A PostgreSQL database stores subscriber and campaign data, providing persistence for the application.
The project uses the `libphonenumber-js` library for phone number validation and formatting. It ensures phone numbers are in the correct format (preferably E.164) and helps prevent issues with SMS delivery due to incorrect formatting.
Tracking consent is a legal requirement for SMS marketing in many jurisdictions (e.g., TCPA, GDPR). It allows you to send messages only to users who have explicitly agreed to receive them, promoting ethical and legal compliance.
Fly.io is suggested as a platform for deploying the full-stack application and database. While deployment details are not included in the provided article section, it is mentioned as a deployment option. It simplifies the deployment process.
Developer Guide: Building SMS Marketing Campaigns with Fastify and Plivo
This guide provides a step-by-step walkthrough for building a production-ready SMS marketing campaign application using the Fastify framework for Node.js and Plivo's messaging platform APIs. We will cover everything from initial project setup to deployment and monitoring.
By the end of this tutorial, you will have a functional backend service capable of managing subscriber lists, creating SMS marketing campaigns, and sending bulk messages via Plivo, incorporating best practices for security, error handling, and scalability.
Project Overview and Goals
What We're Building:
We are creating a backend API service using Fastify. This service will manage:
Problem Solved:
This application provides the core infrastructure needed for businesses to leverage SMS marketing effectively. It centralizes subscriber management, ensures consent is handled, and integrates with a reliable communication platform (Plivo) for message delivery.
Technologies Used:
System Architecture:
Prerequisites:
Final Outcome:
A containerized Fastify application exposing RESTful API endpoints for managing subscribers and campaigns, capable of sending SMS messages via Plivo. The application will include logging, basic error handling, security measures, and deployment configurations.
1. Setting up the Project
Let's initialize our Fastify project and set up the basic structure.
Create Project Directory:
Initialize Node.js Project:
Install Core Dependencies:
fastify
: The core framework.fastify-env
: For managing environment variables.fastify-sensible
: Adds useful decorators likehttpErrors
.fastify-cors
: Enables Cross-Origin Resource Sharing.pino-pretty
: Developer-friendly logger formatting (for development).@prisma/client
: Prisma's database client.plivo
: The official Plivo Node.js SDK.async-retry
: For adding retry logic to Plivo calls.libphonenumber-js
: For robust phone number validation/formatting.fastify-rate-limit
: For API rate limiting.@fastify/helmet
: For security headers.@fastify/sentry
,@sentry/node
,@sentry/tracing
: For error tracking with Sentry.bullmq
,ioredis
: (Optional) Recommended for production-grade background job processing.Install Development Dependencies:
prisma
: The Prisma CLI for migrations and generation.nodemon
: Automatically restarts the server during development.tap
: Fastify's recommended testing framework.Configure
package.json
Scripts: Update thescripts
section in yourpackage.json
:test
: Runs tests usingtap
.start
: Runs the application in production mode.dev
: Runs the application in development mode withnodemon
for auto-reloads,pino-pretty
for readable logs, and the Node inspector attached to127.0.0.1
(localhost only) for security.db:seed
: Runs the database seeding script.prisma:*
: Helper scripts for Prisma operations.Initialize Prisma:
This creates a
prisma
directory with aschema.prisma
file and a.env
file for your database connection string.Configure Environment Variables (
.env
): Update the generated.env
file. Add placeholders for Plivo credentials and other settings..env
file with real secrets to version control. Add.env
to your.gitignore
file.Project Structure: Create the following directory structure:
Basic Fastify App Setup (
src/app.js
):fastify-env
? It validates required environment variables on startup, preventing runtime errors due to missing configuration.fastify-sensible
? Provides standard HTTP error objects (fastify.httpErrors.notFound()
) and other utilities.fastify-autoload
? Simplifies loading plugins and routes from separate directories, keepingapp.js
clean.helmet
andrateLimit
added early.Server Entry Point (
src/server.js
):Add
.gitignore
: Create a.gitignore
file in the root directory:prisma/migrations/*.sql
: This line prevents generated SQL migration files from being committed. This is standard practice as theschema.prisma
file is the source of truth, and migrations are generated from it. If your team workflow requires committing the SQL files for review, you can remove this line.You now have a basic Fastify project structure ready for adding features. Run
npm run dev
to start the development server. You should see log output indicating the server is listening.2. Implementing Core Functionality
We'll start by defining our database schema and creating services for managing subscribers and campaigns.
Define Database Schema (
prisma/schema.prisma
):@unique
onphoneNumber
? Prevents duplicate subscriber entries.isOptedIn
? Crucial for tracking consent, a legal requirement (e.g., TCPA, GDPR).@@index([isOptedIn])
? Added an index to potentially speed up queries filtering by opt-in status, which is common.Apply Database Migrations: Create the initial migration and apply it to your database.
Prisma will create the
Subscriber
andCampaign
tables in your PostgreSQL database.Create Prisma Plugin (
src/plugins/prisma.js
): This plugin initializes the Prisma client and makes it available throughout the Fastify application.fastify-plugin
? Ensures the plugin is loaded correctly and decorators (fastify.prisma
) are available globally.onClose
hook? Ensures the database connection is closed cleanly when the server shuts down.Create Subscriber Service (
src/services/subscriberService.js
): This service encapsulates the logic for interacting with theSubscriber
model, now usinglibphonenumber-js
.libphonenumber-js
for robust validation and formatting to E.164 standard.Create Campaign Service (
src/services/campaignService.js
): (No changes from original, logic remains the same)These services provide a clean abstraction layer over the database operations. We'll use them in our API routes next.
3. Building a Complete API Layer
Now, let's expose our services through Fastify routes with proper request validation.
Subscriber Routes (
src/routes/subscribers.js
):phoneNumber
at the API layer, relying on the service layer for E.164 validation and formatting. Addedformat
hints for CUID and E.164.Campaign Routes (
src/routes/campaigns.js
): (Schema and basic CRUD routes remain similar, focus on the/send
endpoint)