Frequently Asked Questions
You can send SMS messages with NestJS by integrating the Vonage Messages API. Create a NestJS service that uses the Vonage Node.js Server SDK to interact with the API. Expose a POST endpoint in a controller to handle incoming SMS requests and trigger the sending logic implemented in the service.
The Vonage Messages API is a unified API for sending messages through various channels, including SMS. It's known for its robust features, global reach, and developer-friendly tools like the Node.js Server SDK, making it suitable for applications needing to send notifications or alerts.
NestJS provides a structured and efficient framework for building server-side applications in Node.js. Its modular architecture, dependency injection, and features like validation and configuration management make it ideal for integrating with external APIs like Vonage Messages.
First, create a Vonage API account. Then, set the Messages API as the default SMS setting in your Vonage dashboard, which is crucial for this process. You'll need to create a Vonage Application, generate keys, enable Messages capability and link your Vonage number. Finally, configure your environment variables in you .env file.
You'll need Node.js and npm/yarn installed, a Vonage API account (free credits available), and basic knowledge of TypeScript, Node.js, and REST APIs. Familiarity with a terminal/command prompt and optionally Postman or curl for testing are helpful.
Save your private.key file in your project root and reference its path in your .env file, not directly in code. Add both .env and private.key to your .gitignore to prevent accidental commits. Do not embed the key directly into your codebase. In production, use a proper secrets management solution like AWS Secrets Manager or HashiCorp Vault.
Use NestJS's ValidationPipe with Data Transfer Objects (DTOs). Create a DTO (e.g., SendSmsDto) with class-validator decorators (@IsNotEmpty, @IsPhoneNumber, @Length) to define validation rules for properties like recipient number and message text.
A NestJS application with a POST endpoint (/sms/send) that accepts recipient phone number and message text, sending the message via Vonage Messages API. You'll have error handling, logging, and configuration management in place, which are essential for production-ready applications.
Consider Vonage Messages API when you need a unified API across multiple messaging channels, not just SMS. Its robust features, global reach, and well-maintained SDKs are advantages for serious applications.
This tutorial covers only sending SMS. Receiving SMS and delivery receipts require webhooks, a more advanced topic covered in Vonage documentation but beyond the scope of this guide.
Use try...catch blocks in your service to handle errors from the Vonage SDK. Log these errors with a NestJS Logger and throw specific HttpExceptions at the controller level with appropriate status codes (e.g., 500 Internal Server Error) and detailed error messages for clients.
For unit tests, mock the ConfigService, Vonage SDK (vonage.messages.send), and the 'fs' module using Jest. Test success and failure scenarios for the sendSms method. For end-to-end (e2e) tests, use supertest to make real HTTP requests and check responses, including validation errors and service layer errors.
Protect your Vonage API credentials, especially the private key. Use robust input validation to prevent injection attacks and malformed data. Implement rate limiting using @nestjs/throttler to prevent abuse and unexpected costs. Add authentication/authorization to your /sms/send endpoint to control access.
Send SMS with NestJS and Vonage Messages API: Complete Tutorial
Quick Answer: Send SMS with NestJS and Vonage in 5 Steps
To send SMS with NestJS and Vonage: (1) Install
@vonage/server-sdkvia npm, (2) Configure Vonage Application ID and private key using NestJS ConfigModule, (3) Create an SMS service that initializes the Vonage client, (4) Build a controller with a validated POST endpoint (/sms/send), (5) Callvonage.messages.send()with proper error handling. This complete tutorial covers setup, authentication, validation, testing, and production deployment.This comprehensive guide walks you through building a production-ready NestJS application for sending SMS messages using the Vonage Messages API. You'll learn project setup, Vonage authentication, service implementation, API endpoint creation, input validation, error handling, security best practices, testing strategies, and deployment considerations.
By the end of this tutorial, you will have a fully functional NestJS SMS service with a REST API endpoint that sends text messages reliably through Vonage. This solves common business needs like sending notifications, alerts, verification codes, and OTPs (One-Time Passwords) programmatically.
Technologies Used:
Prerequisites:
curlfor testing API endpoints.Final Outcome:
A production-ready NestJS application with a POST endpoint (
/sms/send) that accepts a recipient phone number and message text, validates the input, and sends the SMS via the Vonage Messages API with comprehensive error handling.(Note: This guide focuses on sending SMS messages. To receive inbound SMS or delivery receipts, you need to configure webhooks, which is covered in the Vonage webhooks documentation but is beyond the scope of this tutorial.)
1. Setting up the NestJS Project
First, create a new NestJS project and install the necessary dependencies for SMS functionality.
Install NestJS CLI (Command Line Interface): If you don't have it installed globally, run:
Create NestJS Project: Navigate to your desired parent directory in the terminal and run:
Choose your preferred package manager (npm or yarn) when prompted. This command scaffolds a new NestJS project with a standard structure including TypeScript configuration, testing setup, and essential dependencies.
Navigate to Project Directory:
Install Dependencies: Install the Vonage Server SDK and NestJS's configuration module.
@vonage/server-sdk: The official Vonage library for Node.js.@nestjs/config: For managing environment variables within NestJS applications.dotenv: To load environment variables from a.envfile during development.Project Structure Overview:
Your initial project structure will look something like this:
You will primarily work within the
srcdirectory, creating new modules, controllers, and services for SMS functionality.2. Configuring Vonage Messages API Credentials
To interact with the Vonage API, you need proper authentication credentials and a Vonage phone number. The Vonage Messages API requires an Application ID and a private key for secure authentication.
Log in to Vonage Dashboard: Access your Vonage API Dashboard.
Find API Key and Secret: Your API Key and Secret are displayed at the top of the dashboard home page. Note these down as you may need them for certain operations or when using the Vonage CLI tool.
Set Messages API as Default (Crucial):
Create a Vonage Application:
private.keyfile. Save this file securely – you'll place it in your project root later. The public key is stored by Vonage automatically.https://example.com/status,https://example.com/inbound). These are required fields for receiving messages or delivery receipts, but not strictly necessary for sending only. However, Vonage requires valid HTTPS URLs here.Link a Vonage Number:
14155550100).Configure Environment Variables:
private.keyfile you downloaded into the root directory of yourvonage-sms-appproject..envin the project root directory.VONAGE_APPLICATION_ID: The unique ID of the Vonage application you created. Found on the Application details page in the dashboard.VONAGE_PRIVATE_KEY_PATH: The relative path from your project root to theprivate.keyfile you downloaded.VONAGE_NUMBER: The Vonage virtual phone number you linked to the application, in E.164 format (e.g.,14155550100). This will be the "from" number for outgoing SMS messages.VONAGE_API_KEY/VONAGE_API_SECRET: Your main account API key/secret. Not directly used for sending via Messages API in this code, but good to store for other potential uses (like CLI operations or other Vonage services).PORT: The port your NestJS application will run on (default is 3000).Update
.gitignore: Ensure your.envfile andprivate.keyare not committed to version control. Add these lines to your.gitignorefile if they aren't already present:Load Configuration in NestJS: Modify
src/app.module.tsto load the.envfile usingConfigModule.ConfigModule.forRoot({...}): Initializes the configuration module.isGlobal: true: Makes theConfigServiceavailable throughout your application without needing to importConfigModulein every feature module.envFilePath: '.env': Tells the module where to find the environment variables file.3. Implementing the SMS Service with Dependency Injection
Now, create a dedicated module and service in NestJS to handle the logic for sending SMS messages using proper dependency injection patterns.
Generate SMS Module and Service: Use the NestJS CLI to generate the necessary files:
This creates a
src/smsdirectory containingsms.module.tsandsms.service.ts(and spec file for testing).Implement
SmsService: Opensrc/sms/sms.service.tsand implement the logic to initialize the Vonage SDK and send messages.ConfigServiceto retrieve environment variables. Initializes theVonageSDK instance using the Application ID and the content of the private key file (read usingfs.readFileSync). It includes checks to ensure credentials and the key file exist.sendSmsMethod:to) and messagetextas arguments.tonumber format.payloadobject according to the Vonage Messages API requirements for SMS (message_type: 'text',channel: 'sms',to,from,text).this.vonage.messages.send(payload)to send the SMS. Note the use ofmessages, notmessage.async/awaitfor cleaner asynchronous code.Logger.message_uuidon success or throws an error on failure.Update
SmsModule: EnsureSmsServiceis listed as a provider and exported so it can be used in other modules (like the controller you'll create next).Import
SmsModuleinAppModule: Now uncomment theSmsModuleimport insrc/app.module.ts.4. Building the SMS API Endpoint with Validation
Create a controller with an endpoint that accepts POST requests to trigger the SMS sending functionality with proper input validation.
Generate SMS Controller:
This creates
src/sms/sms.controller.ts(and spec file for testing).Create Request DTO (Data Transfer Object): Create a file
src/sms/dto/send-sms.dto.tsfor validating the incoming request body. Install validation dependencies if you haven't already:Note: This uses class-validator v0.14.2 (May 2024) and class-transformer (actively maintained), which are the most commonly used validation packages in NestJS due to their decorator-based approach that integrates seamlessly with NestJS's ValidationPipe.
Now, define the DTO:
class-validatorto define validation rules.@IsNotEmpty(): Ensures the field is not empty.@IsPhoneNumber(null): Validates if the string is a phone number (accepts various formats, but E.164 is recommended for Vonage). Setting region tonullallows international numbers.@IsString(): Ensures the field is a string.@Length(1, 1600): Ensures the message text has a reasonable length. Vonage SMS limits:Implement
SmsController: Opensrc/sms/sms.controller.tsand define the POST endpoint.@Controller('sms'): Defines the base route for this controller.SmsService.@Post('send'): Defines a handler for POST requests to/sms/send.@UsePipes(new ValidationPipe(...)): Applies the validation pipe to this route.transform: true: Automatically transforms the incoming JSON payload into an instance ofSendSmsDto.whitelist: true: Strips any properties from the request body that are not defined in the DTO.@Body() sendSmsDto: SendSmsDto: Injects the validated and transformed request body into thesendSmsDtoparameter.smsService.sendSmsmethod. Returns a success response with themessageId. NestJS handles the 201 status. Throws anHttpExceptionwith a 500 status code if the service throws an error.Register Controller in
SmsModule: Uncomment theSmsControllerinsrc/sms/sms.module.ts.Enable Global Validation Pipe (Optional but Recommended): Instead of applying
@UsePipesto every handler, you can enable theValidationPipeglobally insrc/main.ts.If you do this, you can remove the
@UsePipes(...)decorator from theSmsController.5. Error Handling and Logging Best Practices
You've already incorporated basic logging and error handling:
Loggerinstances in both the service and controller provide context-specific logs for requests, successes, and failures. Check your console output when running the app.ValidationPipeautomatically handles request validation errors, returning 400 Bad Request responses with details about the validation failures.SmsServicecatches errors from the Vonage SDK (try...catch) and logs them before re-throwing.SmsControllercatches errors from the service and transforms them into standardHttpExceptionresponses, ensuring consistent JSON error formats for the client.Further Enhancements:
6. Security Considerations for Production SMS Services
While this is a simple service, security is paramount:
.envfile orprivate.keyto Git. Ensure they are in.gitignore..envfiles. Inject these secrets as environment variables into your deployed application.private.keyon your server (chmod 400 private.key).ValidationPipeandSendSmsDto. This prevents malformed requests and potential injection issues related to input data.forbidNonWhitelisted: truein the global pipe adds an extra layer of protection against unexpected input fields.@nestjs/throttlermodule is excellent for this.npm install @nestjs/throttlerapp.module.ts:ttlandlimitas needed./sms/sendendpoint itself. In a real-world application, protect this endpoint using strategies like API Keys, JWT tokens, or OAuth, ensuring only authorized clients can trigger SMS sending. NestJS provides modules and strategies for implementing these. For more details, see the NestJS authentication documentation.7. Testing the SMS Implementation (Unit and E2E Tests)
Testing ensures your service works as expected and helps prevent regressions.
Unit Testing (
SmsService):sms.service.spec.ts).@nestjs/testingto create a testing module.ConfigService,VonageSDK,fs). Jest's mocking capabilities are ideal here.sendSmsmethod:vonage.messages.sendwith the correct payload.sendto resolve successfully).sendto reject with an error).Example Snippet:
E2E (End-to-End) Testing (
SmsController):testdirectory.@nestjs/testingandsupertestto make HTTP requests to your running application instance (or a test instance)./sms/sendendpoint:SmsService.sendSmsto reject) and check for 500 Internal Server Error responses.Example Snippet (Conceptual):
Frequently Asked Questions (FAQ)
What is the difference between Vonage SMS API and Messages API?
The Vonage SMS API is the legacy API for sending SMS messages, while the Messages API is the newer, unified API that supports multiple channels (SMS, MMS, WhatsApp, Viber, Facebook Messenger) through a single interface. The Messages API uses Application ID and private key authentication instead of API Key/Secret, provides better error handling, and is the recommended approach for new applications. This tutorial uses the Messages API for maximum flexibility and future compatibility.
How much does it cost to send SMS with Vonage?
Vonage SMS pricing varies by destination country and typically ranges from $0.0037 to $0.15 per message. New accounts receive free trial credits (usually $2 – $10) to test the service. Pricing is per-segment: messages up to 160 GSM-7 characters or 70 unicode characters count as one segment, while longer messages split into 153-character (GSM-7) or 67-character (unicode) segments with each segment billed separately. Check the Vonage Pricing page for current rates in your target countries.
Can I use Vonage with NestJS for two-factor authentication (2FA)?
Yes, you can use Vonage with NestJS for 2FA/OTP verification. However, Vonage offers a dedicated Verify API specifically designed for 2FA that handles OTP generation, delivery, retry logic, and verification automatically. For simple SMS-based 2FA, you can use this tutorial's implementation to send randomly generated codes, then store and validate them server-side. For production 2FA, consider using Vonage Verify API, which provides built-in rate limiting, fraud detection, and fallback channels (SMS → voice call).
How do I receive SMS messages and delivery receipts in NestJS?
To receive inbound SMS messages and delivery receipts, you need to create webhook endpoints in your NestJS application. Create a new controller with POST endpoints for inbound messages (
/webhooks/inbound-sms) and delivery receipts (/webhooks/delivery-receipt), configure these URLs in your Vonage Application settings (they must be publicly accessible HTTPS URLs), and use DTOs withclass-validatorto validate the webhook payloads. For local development, use ngrok to expose your localhost to the internet. The webhook payloads contain message status, timestamps, error codes, and sender information.What is E.164 phone number format and why does Vonage require it?
E.164 is the international standard for phone number formatting:
+[country code][subscriber number]with no spaces, hyphens, or parentheses (e.g.,+14155550100for a US number). Vonage requires E.164 format because it eliminates ambiguity about country codes and ensures reliable message delivery worldwide. The@IsPhoneNumber(null)validator fromclass-validatoraccepts E.164 format. Always store and transmit phone numbers in E.164 format, but you can display them with formatting for user interfaces.How do I handle SMS sending errors in NestJS with Vonage?
The Vonage SDK throws errors with specific properties that help identify issues. Common errors include: Authentication failures (invalid Application ID or private key, HTTP 401), Insufficient credit (account balance too low, HTTP 402), Invalid phone numbers (malformed recipient, HTTP 400), Rate limiting (too many requests, HTTP 429), and Network timeouts (connectivity issues). Wrap
vonage.messages.send()calls intry...catchblocks, log errors with context using NestJS Logger, inspecterror.response?.datafor Vonage-specific error details, throw appropriateHttpExceptioninstances with user-friendly messages, and implement retry logic for transient errors using libraries likeasync-retry.Can I send bulk SMS messages with this NestJS implementation?
This tutorial's implementation sends single SMS messages per API call. For bulk messaging (hundreds or thousands of recipients), you should: Use Vonage Campaigns API for marketing messages (supports opt-out management and scheduling), implement queue-based processing using Bull or BullMQ to avoid overwhelming your API with concurrent requests, add rate limiting logic to respect Vonage's API rate limits (typically 30 – 100 requests/second depending on your account tier), use Promise.all() with batching for parallel sends with controlled concurrency, and store message results in a database for tracking delivery status and failures. Consider Vonage's Bulk SMS or Campaigns API for large-scale messaging needs.
How do I test SMS sending without actually sending messages?
For testing without sending real SMS messages: Mock the Vonage SDK in unit tests using Jest's
jest.mock()to simulate successful sends and error conditions, Use Vonage's test credentials (some SMS APIs provide sandbox/test modes, though Vonage primarily uses real credentials), Send to your own phone numbers during development (free trial credits cover testing), Implement a "dry run" mode with an environment variable (SMS_DRY_RUN=true) that logs intended sends without calling the Vonage API, and Use E2E tests with mocked service layer to test controller validation and error handling without actual API calls (see Section 7 for examples).What are the SMS character limits and encoding types?
Vonage supports two encoding types: GSM-7 (standard English alphabet, numbers, basic symbols) allows 160 characters per single message or 153 characters per segment for concatenated messages (7-byte UDH header overhead), while UCS-2/Unicode (emoji, non-Latin scripts, special characters) allows 70 characters per single message or 67 characters per segment for concatenated messages. Messages automatically use UCS-2 if they contain any unicode characters, which significantly reduces the character limit. To maximize efficiency: avoid emoji in transactional SMS, use GSM-7 characters when possible (check GSM-7 character set), and be aware that some characters like
[]{}|^€~\\count as 2 characters in GSM-7 encoding.How do I deploy this NestJS SMS service to production?
To deploy your NestJS SMS service to production: Use environment-based secrets management (AWS Secrets Manager, Azure Key Vault, Google Secret Manager) instead of
.envfiles, Enable HTTPS using load balancers, reverse proxies (Nginx, Caddy), or platforms with built-in SSL (Heroku, Vercel), Implement rate limiting with@nestjs/throttlerto prevent abuse and control costs, Add authentication (JWT, API keys, OAuth) to protect the/sms/sendendpoint, Configure structured logging (Winston, Pino) with log aggregation (Datadog, CloudWatch, ELK), Set up monitoring and alerts for SMS failures, rate limit hits, and cost thresholds, Use PM2 or Docker for process management and zero-downtime deployments, and Implement retry logic and dead letter queues for handling transient failures. Consider platforms like AWS ECS, Google Cloud Run, or DigitalOcean App Platform for containerized deployments.Recap and Next Steps
What You've Built:
You've created a production-ready NestJS application that:
POST /sms/send) for sending SMS messagesNext Steps:
/sms/sendendpoint with API keys, JWT tokens, or OAuthFor more information, consult the Vonage API Documentation and the NestJS Documentation.