Building a Call Scheduling Service with Twilio and NestJS
Time to read: 7 minutes
Building a Call Scheduling Service with Twilio and NestJS
Introduction
This guide demonstrates how to build a call scheduling service using Twilio Programmable Voice and the NestJS framework, emphasizing its modular architecture with Controllers, Services, and Modules. This structure supports scalability, maintainability, and microservices readiness, integrating Twilio APIs for scheduling voice calls while ensuring quality through unit tests
Prerequisites
To follow along, ensure you have the following:
- Node.js (v16 or later) and npm installed.
- An active Twilio account.
- Twilio credentials: Account SID, Auth Token, and a Twilio phone number.
- A code editor (e.g., VS Code).
Refer to the GitHub Repository for the final code.
Installing the Nest CLI
Run the following command on your system to install the Nest CLI, which we’ll use to scaffold a project template and add resources to our application as we go:
Scaffold the Project
On your terminal, navigate to your preferred project directory and start by creating a new NestJS project named twilio-scheduler
:
When prompted, choose yarn
as the package manager, as we’ll be using it for this tutorial. You’re also free to pick another option that you’re more comfortable with, but if doing so please note that you’ll have to use its own yarn-equivalents for most of the commands in this guide.
Once the nest installation is complete, navigate into the project directory:
Install the required dependencies:
Integrating Twilio
Setting Up Twilio Credentials
Create a file named .env
in the project root and add your Twilio credentials to it:
These credentials can be found on your Twilio Console.


Also, create a file at src/common/enums/environment.enum.ts to hold the string keys of these variables:
And to access these environment variables in your Nest application, update the src/app.module.ts file:
Creating the Twilio Service
Start off by generating the Twilio service using the CLI:
Writing unit tests for the Twilio Service
Before implementing the Twilio service, we write tests to ensure it behaves as expected:
This test suite ensures the TwilioService behaves as expected before implementation, focusing on key functionalities and error handling. Using TestingModule, it mocks the ConfigService to provide environment variables such as TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER. This isolates the service from external dependencies, making the tests reliable and predictable.
Key aspects of these tests include:
- Client Initialization: Verifies that the Twilio client is created with the correct credentials by ensuring the mocked environment variables are accessed as expected.
- Phone Number Retrieval: Confirms that the service correctly returns the Twilio phone number from the configuration.
- Error Handling: Tests the service’s behavior when a required environment variable is missing, ensuring it throws a meaningful error. This helps identify configuration issues early.
Implementing the Twilio Service
In src/twilio/twilio.service.ts, implement the base Twilio logic:
This code implements the core logic for the TwilioService, responsible for interacting with the Twilio API. It encapsulates Twilio client initialization and provides access to its key components.
Key highlights of the implementation:
- Dependency Injection: The ConfigService is injected into the constructor to retrieve essential environment variables such as TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER. These values are used to configure the Twilio client.
- Validation: The constructor checks if all required environment variables are defined. If any are missing, it throws an error, preventing the service from running with incomplete configuration.
- Initialization: The Twilio client is instantiated with the accountSid and authToken, ensuring the service is ready to make API calls. A Logger instance logs the successful initialization for debugging and monitoring purposes.
- Getter Methods:
- getClient() provides access to the initialized Twilio client for making API calls.
- getTwilioPhoneNumber() returns the configured Twilio phone number, ensuring this value is accessible elsewhere in the application.
Let’s run the tests to ensure functionality. But before we can do so, we must configure JEST to be compatible with our src/* imports.
In the Typescript configuration, add the following:
And in the JEST configuration in the package.json file:
With that done, you can now run tests:
Integrating Programmable Voice
Creating the Programmable Voice Service
Let’s use the CLI to scaffold the programmable voice service files:
Like before, we’ll begin by writing unit tests for the Twilio Programmable Voice service:
This test suite verifies the functionality of TwilioProgrammableVoiceService, focusing on its interaction with the Twilio API. It uses NestJS's TestingModule to mock the TwilioService dependency, ensuring the tests remain isolated from external services.
- Mocking Twilio Client:
- A mock Twilio client is provided to simulate the behavior of the real API.
- The calls.create method is mocked to return a resolved value containing a sid for test validation, avoiding actual API calls.
- Service Initialization:
- The TwilioService is mocked to provide predefined responses for getClient() and getTwilioPhoneNumber(). This ensures the TwilioProgrammableVoiceService can operate with the mocked Twilio client.
- Tests:
- Service Definition: Verifies that the TwilioProgrammableVoiceService is defined, ensuring the test setup is correct.
- API Call Verification:
- Tests the makeVoiceCall method to confirm it interacts with the Twilio API correctly.
- Validates that the calls.create method is called with the appropriate parameters, including the to number, from number, and the generated TwiML for the call message.
Implementing the Programmable Voice Service
In the Twilio Programmable Voice service source file, define the logic for initiating calls:
This implementation of TwilioProgrammableVoiceService handles voice call initiation using the Twilio SDK. The makeVoiceCall method takes a to number and message, retrieves the client and phone number from TwilioService, and uses them to create a call with a dynamically generated <Say> message. logging tracks call initiation, success (logging the call SID), and errors, which are re-thrown after being logged for debugging. This ensures clean, modular, and maintainable call logic
Run the tests now to ensure functionality:
Creating the Reminders Resource
We will create a reminders
resource to handle scheduling logic. For simplicity, we’ll only add an endpoint to create a reminder, but as an exercise you can add endpoints to fetch, update, and delete reminders.
Generate the Resource
Run the following command to scaffold the resource:
Choose REST API
and enable CRUD endpoints. This will generate the necessary files for a controller, service, and DTOs.
Writing Tests for the Reminders Controller
Before implementing the controller, write tests to validate its functionality in the Reminders Controller spec file:
This test suite shows how the RemindersController interacts with the RemindersService. The beforeEach block sets up a testing module with mock dependencies, isolating the controller to focus purely on its behavior. This ensures the tests remain independent and targeted.
The createReminder test confirms that the controller correctly delegates tasks to the service. By mocking the service and verifying input/output behavior, we validate the controller's functionality without relying on external components. Such practices strengthen code quality and ensure scalable APIs in NestJS.
Implementing the Reminders Controller
With the tests written, implement the RemindersController
in its source file:
This code defines a NestJS controller for handling reminders, specifically creating them via a POST request to /reminders. It uses the RemindersService (which will be implemented later) to handle business logic and a CreateReminderDto to validate the request body, ensuring clean separation of concerns and modularity.
Let’s also write the code for the CreateReminderDto class:
Implementing the Reminders Service
We’ll now move on to implementing the business logic for the reminders controller. In NestJS lingo, we’ll be implementing a “service”.
Writing Tests for the Service
Like we did before, we’ll start by writing the unit tests for the Reminders service:
This test suite for RemindersService effectively isolates the service logic by using TestingModule to mock dependencies like SchedulerRegistry and TwilioProgrammableVoiceService. Mocking with jest.fn() replaces real implementations, allowing the tests to run quickly and predictably without relying on external systems.
A notable part of the suite is the test for the triggerCall method. It uses jest.spyOn() to mock and track calls to makeVoiceCall in the TwilioProgrammableVoiceService, ensuring the method behaves correctly without triggering actual API calls. This approach keeps the tests focused and allows for validating interactions between components while maintaining modularity and clarity. The setup lays a solid foundation for ensuring the reliability of the service.
Implementing the Service
With tests written, implement the RemindersService
in the source file:
This implementation of RemindersService manages the creation and scheduling of reminders using cron jobs. The create method validates that the scheduledTime is in the future, then sets up a CronJob to execute the triggerCall method at the specified time. The job is registered with the SchedulerRegistry using a unique identifier and started immediately. Logging provides details about the created reminder.
The triggerCall method handles voice call execution through TwilioProgrammableVoiceService. It logs call attempts and captures any errors, ensuring robust error handling. This design cleanly separates scheduling from execution, making the service modular and easy to debug.
Before this can run, we must register the Task Scheduler and Reminders modules in the App Module file:
Run tests once more to ensure functionality:
Testing the Application
Run the application in development mode:
Adding a Caller ID for Trial Accounts
If you are using a Twilio trial account, you must verify your phone number as a Caller ID to allow Twilio to call it. Follow these steps:
1. Log in to the Twilio Console..
2. Navigate to the Phone Numbers section under the Manage tab.
3. Select Verified Caller IDs.
4. Click + Add a new Caller ID.
5. Enter your phone number and click Verify.
6. Twilio will text or call your number and provide a verification code. Enter the code to complete the verification.
Once your number is verified, you can use it in the phone
field when testing the /reminders
endpoint.
Test the POST /reminders
endpoint using curl
:
For MacOS command line:
Alternatively, for Linux users: (the date command is different)
Alternatively, for Windows users:
You should replace "+1234567890" with your actual phone number that will receive the call.


Conclusion
You have successfully built a call scheduling service using NestJS and Twilio. This modular approach ensures scalability and maintainability, allowing for easy expansion into features like SMS reminders, microservices architecture, or recurring schedules.
Further Resources
To deepen your understanding of the technologies used in this project, explore the following resources:
- NestJS Documentation: Learn more about the NestJS framework and its powerful features.
- Twilio Documentation: Explore the Twilio platform, including Programmable Voice and other APIs.
- Programmable Voice API Reference: Understand how to fully leverage Twilio's voice capabilities.
- Jest Documentation: Learn more about writing and running tests with Jest.
By combining these resources with hands-on practice, you can continue to build robust and scalable applications.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.