code examples
code examples
Send SMS with Vonage, Node.js & Fastify: Complete API Tutorial
Learn how to build a high-performance SMS API using Node.js, Fastify framework, and Vonage Messages API. Step-by-step guide with code examples, security best practices, and production-ready patterns.
How to Send SMS Messages with Fastify and Vonage Messages API
Learn how to build a production-ready SMS API endpoint using Node.js, the Fastify web framework, and the Vonage Messages API. This comprehensive tutorial covers project setup, Vonage configuration, API implementation, security features, error handling, and testing—everything you need to send SMS messages programmatically from your Fastify application.
By the end of this guide, you'll have a functional Fastify API endpoint (POST /api/v1/send-sms) that accepts a phone number and message payload, then uses Vonage to deliver the SMS reliably. This solution is ideal for applications requiring transactional SMS, notification systems, or two-factor authentication messaging.
Prerequisites
Before starting this SMS integration tutorial, ensure you have:
- Node.js (LTS version recommended) and npm installed
- A Vonage API account with API credentials
- Basic familiarity with Node.js, asynchronous programming, and REST APIs
- A tool for making HTTP requests (cURL, Postman, or similar)
Technology Stack Overview
This tutorial uses the following technologies for building a robust SMS API:
- Node.js: JavaScript runtime environment for server-side development
- Fastify: A high-performance, low-overhead web framework for Node.js, chosen for its exceptional speed, extensibility, and developer experience
- Vonage Messages API: Enterprise-grade API for sending messages across multiple channels including SMS, providing reliable message delivery with global reach
@vonage/server-sdk: Official Vonage Node.js SDK for seamless API interactiondotenv: Environment variable management module required byfastify-envfastify-env: Fastify plugin for loading and validating environment variables securelyfastify-rate-limit: Built-in rate limiting plugin for API protection@sinclair/typebox: TypeScript-based schema validation library for Fastify routes and environment variables
System Architecture
Understanding the SMS API flow helps you implement and troubleshoot effectively:
- A client application (Postman, cURL, or your frontend) sends a POST request to the Fastify API endpoint (
/api/v1/send-sms) - The Fastify application receives the request and validates input data (phone number format, message content)
- The application uses the Vonage Node.js SDK, configured with your Vonage Application ID and Private Key, to call the Vonage Messages API
- Vonage's infrastructure handles SMS delivery to the recipient's phone number via carrier networks
- The Vonage API returns a response containing the message ID for tracking
- The Fastify application sends a success or error response back to the client
Final Outcome: A production-ready Node.js Fastify API server with a single SMS endpoint (POST /api/v1/send-sms) that securely sends text messages using Vonage's reliable messaging infrastructure.
1. Setting up the Node.js Project
Let's initialize the project structure and install all required dependencies for the SMS API.
Create Project Directory
Open your terminal and create a new directory for the Fastify SMS project:
mkdir fastify-vonage-sms
cd fastify-vonage-smsInitialize Node.js Project
Create a package.json file to manage dependencies:
npm init -yInstall Required Dependencies
Install Fastify, the Vonage SDK, and supporting packages for environment management, rate limiting, and validation:
npm install fastify @vonage/server-sdk dotenv fastify-env fastify-rate-limit @sinclair/typeboxInstall pino-pretty as a development dependency for readable log output during development:
npm install --save-dev pino-prettySet up Project Structure
Create a well-organized project structure following Node.js best practices:
fastify-vonage-sms/
├── node_modules/
├── src/
│ ├── plugins/
│ │ └── env.js # Environment variable configuration
│ ├── routes/
│ │ └── sms.js # SMS sending route
│ ├── services/
│ │ └── vonage.js # Vonage SDK interaction logic
│ └── app.js # Fastify application setup
├── .env.example # Example environment variables
├── .env # Local environment variables (DO NOT COMMIT)
├── .gitignore # Git ignore file
├── package.json
└── server.js # Server entry pointDirectory explanation:
src/: Contains core application logicplugins/: Fastify plugins for environment variable handling and other cross-cutting concernsroutes/: API endpoint definitionsservices/: External service integrations like Vonageapp.js: Configures and exports the Fastify instanceserver.js: Application entry point that starts the server.env/.env.example: Environment variable management for API credentials
Create .gitignore
Prevent committing sensitive information and build artifacts by creating a .gitignore file:
# .gitignore
node_modules
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*Create .env.example
Create an .env.example file as a template for required environment variables:
# .env.example
# Server Configuration
PORT=3000
HOST=0.0.0.0
# Vonage API Credentials & Configuration
# Found in your Vonage Dashboard -> API Settings
VONAGE_API_KEY=YOUR_VONAGE_API_KEY
VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET
# Vonage Application Credentials (for Messages API)
# Create an application in Vonage Dashboard -> Applications
VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID
VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to project root OR the key content itself
# Vonage Number or Sender ID
# Must be a Vonage virtual number associated with your application
# or an approved Alphanumeric Sender ID
VONAGE_SMS_FROM=YOUR_VONAGE_NUMBER_OR_SENDER_IDCreate Local .env File
Duplicate .env.example to create your local configuration file:
cp .env.example .envSecurity Note: Never commit your .env file to version control. Add your actual Vonage credentials in the next section.
2. Integrating with Vonage Messages API
Before implementing the SMS functionality, configure your Vonage account and obtain the necessary API credentials.
Sign Up or Log In to Vonage
Ensure you have a Vonage API account. New accounts typically receive free credit for testing SMS functionality.
Set Messages API as Default
Configure Vonage to use the Messages API for SMS:
- Navigate to your Vonage API Dashboard
- Go to Settings
- Under API Keys → SMS Settings, set "Default SMS Setting" to Messages API
- Click Save changes
This ensures consistency across all Vonage features and enables access to advanced messaging capabilities.
Get API Key and Secret
Retrieve your basic API credentials:
- Find your API Key and API Secret at the top of the Vonage API Dashboard
- Copy these credentials
- Add them to your
.envfile forVONAGE_API_KEYandVONAGE_API_SECRET
Note: While the Messages API primarily uses Application ID and Private Key for authentication, the SDK may require API Key/Secret for initialization.
Create a Vonage Application
The Messages API requires a Vonage Application to manage authentication and webhook settings:
- Navigate to Applications in the Vonage Dashboard
- Click Create a new application
- Enter a descriptive name (e.g., "Fastify SMS Sender")
- Under Capabilities, enable Messages
- For Inbound URL and Status URL, enter placeholder URLs (e.g.,
https://example.com/inbound,https://example.com/status) since we're only sending SMS in this tutorial - Click Generate public and private key – this downloads a
private.keyfile - Save this file securely in your project root directory (
fastify-vonage-sms/private.key) - Important: Do not commit this file to version control
- Click Generate new application
- Copy the Application ID from the application page
- Add the Application ID to your
.envfile forVONAGE_APPLICATION_ID - Update
VONAGE_PRIVATE_KEY_PATHin.envto the correct path (e.g.,./private.key)
Link a Vonage Number
You need a Vonage virtual number as the sender for outbound SMS:
- On your application page, scroll to Link virtual numbers
- Click Link next to an available Vonage number
- If you don't have a number, purchase one via Numbers → Buy numbers
- Copy the linked number in E.164 format (e.g.,
14155550100) - Add this to your
.envfile forVONAGE_SMS_FROM
Alternative: If approved for your account, use an Alphanumeric Sender ID (e.g., "MyApp").
Whitelist Test Numbers (Trial Accounts Only)
Trial Vonage accounts can only send SMS to verified numbers:
- Navigate to Numbers → Test numbers
- Add destination phone numbers you'll use for testing
- Verify ownership via the code sent to each number
Your .env file should now contain all required Vonage credentials for SMS integration.
3. Implementing Core SMS Functionality
Now we'll build the Fastify server, configure environment variables, create the Vonage service layer, and define the SMS API route.
Configure Environment Variables Plugin
Create src/plugins/env.js to load and validate environment variables:
// src/plugins/env.js
import fp from 'fastify-plugin';
import fastifyEnv from 'fastify-env';
import { Type } from '@sinclair/typebox'; // For schema validation
const schema = Type.Object({
PORT: Type.Number({ default: 3000 }),
HOST: Type.String({ default: '0.0.0.0' }),
VONAGE_API_KEY: Type.String(),
VONAGE_API_SECRET: Type.String(),
VONAGE_APPLICATION_ID: Type.String(),
VONAGE_PRIVATE_KEY_PATH: Type.String(), // Can be path or key content
VONAGE_SMS_FROM: Type.String(),
});
async function configLoader(fastify, options) {
await fastify.register(fastifyEnv, {
dotenv: true, // Load .env file using dotenv
schema: schema,
confKey: 'config', // Access variables via fastify.config
});
}
export default fp(configLoader);Key features:
- Defines validation schema using
@sinclair/typeboxto specify expected types dotenv: trueenables automatic.envfile loadingconfKey: 'config'makes variables accessible viafastify.config- Type validation ensures all required credentials are present before starting
Create Vonage Service Module
Create src/services/vonage.js to encapsulate Vonage SDK initialization and SMS sending logic:
// src/services/vonage.js
import { Vonage } from '@vonage/server-sdk';
import path from 'path';
import { promises as fs } from 'fs';
let vonage;
// Function to initialize Vonage SDK asynchronously
async function initializeVonage(config, log) {
if (vonage) {
return vonage;
}
try {
let privateKeyContent;
const keyPathOrContent = config.VONAGE_PRIVATE_KEY_PATH;
// Check if it looks like a file path or the key content itself
if (keyPathOrContent.startsWith('-----BEGIN PRIVATE KEY-----')) {
log.info('Using private key content directly from environment variable.');
privateKeyContent = keyPathOrContent;
} else {
log.info(`Attempting to read private key file from path: ${keyPathOrContent}`);
// Construct the absolute path to the private key file
const privateKeyPath = path.resolve(process.cwd(), keyPathOrContent);
// Check if the private key file exists before reading
try {
await fs.access(privateKeyPath);
// Read the private key content
// The SDK expects the key content as a string or buffer
privateKeyContent = await fs.readFile(privateKeyPath);
log.info(`Successfully read private key file from: ${privateKeyPath}`);
} catch (err) {
log.error({ err }, `Vonage Private Key file access error at path: ${privateKeyPath}`);
throw new Error(`Vonage Private Key file not found or inaccessible at path specified in VONAGE_PRIVATE_KEY_PATH: ${keyPathOrContent}`);
}
}
if (!privateKeyContent) {
throw new Error('Vonage Private Key content could not be determined.');
}
vonage = new Vonage({
apiKey: config.VONAGE_API_KEY,
apiSecret: config.VONAGE_API_SECRET,
applicationId: config.VONAGE_APPLICATION_ID,
privateKey: privateKeyContent, // Pass the key content directly
});
log.info('Vonage SDK initialized successfully.');
return vonage;
} catch (error) {
log.error('Failed to initialize Vonage SDK:', error);
// Throwing error to prevent app from starting with invalid config
throw new Error(`Vonage SDK initialization failed: ${error.message}`);
}
}
// Function to send SMS using the Messages API
async function sendSms(to, text, config, log) {
const vonageInstance = await initializeVonage(config, log); // Ensure SDK is initialized
try {
const resp = await vonageInstance.messages.send({
message_type: 'text',
to: to,
from: config.VONAGE_SMS_FROM,
channel: 'sms',
text: text,
});
log.info(`SMS sent successfully to ${to}. Message UUID: ${resp.message_uuid}`);
return resp; // Contains message_uuid
} catch (err) {
// Log detailed error information from Vonage if available
const errorDetails = err?.response?.data || { message: err.message, detail: err.detail };
log.error({
message: `Error sending SMS to ${to}`,
vonageError: errorDetails,
statusCode: err?.response?.status
}, `Vonage API Error: ${errorDetails.title || errorDetails.message || 'Unknown error'}`);
// Rethrow a structured error for the route handler
throw new Error(`Vonage API Error: ${errorDetails.title || errorDetails.detail || errorDetails.message || 'Unknown error sending SMS'}`);
}
}
export { initializeVonage, sendSms };Implementation highlights:
initializeVonage: Handles both file-based and inline private key content, with proper error handlingsendSms: Callsvonage.messages.sendwith required parameters for SMS channel- Enhanced error logging captures detailed Vonage API error responses
- Uses
path.resolveandprocess.cwd()for reliable file path handling
Create SMS API Route
Create src/routes/sms.js to define the SMS sending endpoint:
// src/routes/sms.js
import { Type } from '@sinclair/typebox';
import { sendSms as sendVonageSms } from '../services/vonage.js';
// Schema for validating the request body
const sendSmsBodySchema = Type.Object({
to: Type.String({
description: 'Recipient phone number in E.164 format (e.g., +14155550100)',
// Basic E.164 pattern check (starts with +, followed by 1-15 digits)
pattern: '^\\+[1-9]\\d{1,14}$'
}),
message: Type.String({
description: 'The text message content',
minLength: 1,
maxLength: 1600 // Standard SMS limit consideration
}),
}, {
additionalProperties: false // Disallow properties not defined in the schema
});
// Schema for the success response
const sendSmsResponseSchema = Type.Object({
success: Type.Boolean(),
message_uuid: Type.String(),
message: Type.String(),
});
// Optional: Schema for error responses
const errorResponseSchema = Type.Object({
success: Type.Boolean({ default: false }),
message: Type.String()
});
async function smsRoutes(fastify, options) {
// Expose config and logger from fastify instance
const { config, log } = fastify;
fastify.post('/send-sms', {
schema: {
description: 'Sends an SMS message via Vonage Messages API',
tags: ['SMS'],
summary: 'Send an SMS',
body: sendSmsBodySchema,
response: {
200: sendSmsResponseSchema,
// Define potential error responses for documentation/validation
400: errorResponseSchema, // Validation errors, potentially some Vonage errors
500: errorResponseSchema // Server/Vonage errors
}
}
}, async (request, reply) => {
const { to, message } = request.body;
log.info(`Received request to send SMS to: ${to}`);
try {
// Call the Vonage service function
const result = await sendVonageSms(to, message, config, log);
reply.code(200).send({
success: true,
message_uuid: result.message_uuid,
message: `SMS submitted successfully to ${to}`,
});
} catch (error) {
// Log the error with context
log.error({ err: error }, `Failed to send SMS to ${to}: ${error.message}`);
// Determine appropriate status code based on error type if possible
// Example: Check for specific Vonage error messages
if (error.message && error.message.includes("Non-Whitelisted")) {
reply.code(400).send({ success: false, message: 'Destination number not whitelisted for trial account.' });
} else if (error.message && error.message.includes("Authentication Failed")) {
reply.code(401).send({ success: false, message: 'Vonage authentication failed. Check credentials.' });
} else {
// Default to 500 for other errors
reply.code(500).send({
success: false,
message: error.message || 'An internal server error occurred while sending the SMS.',
});
}
}
});
// Basic health check endpoint
fastify.get('/health', {
schema: {
description: 'Checks the health of the service',
tags: ['Health'],
summary: 'Health Check',
response: {
200: Type.Object({
status: Type.String(),
timestamp: Type.String({ format: 'date-time' })
})
}
}
}, async (request, reply) => {
return { status: 'ok', timestamp: new Date().toISOString() };
});
}
export default smsRoutes;Route features:
- Strict E.164 phone number validation using regex pattern
- Message length validation (1-1600 characters)
additionalProperties: falseprevents unexpected fields- Intelligent error handling with appropriate HTTP status codes
- Health check endpoint for monitoring
Set up Fastify Application
Create src/app.js to build the Fastify instance and register plugins:
// src/app.js
import Fastify from 'fastify';
import configLoader from './plugins/env.js';
import smsRoutes from './routes/sms.js';
import { initializeVonage } from './services/vonage.js';
import fastifyRateLimit from 'fastify-rate-limit';
async function buildApp() {
const fastify = Fastify({
logger: {
level: process.env.LOG_LEVEL || 'info', // Use env var or default
// Use pino-pretty only if not in production for better performance
transport: process.env.NODE_ENV !== 'production' ? {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
} : undefined, // Use default JSON output in production
},
});
// Register environment variable plugin FIRST
await fastify.register(configLoader);
// Initialize Vonage SDK after config is loaded
// Ensures config is available and handles potential async init errors on startup
try {
await initializeVonage(fastify.config, fastify.log);
} catch (err) {
// Use console.error as logger might not be fully ready if init fails early
console.error("Critical error during Vonage SDK initialization. Exiting.", err);
process.exit(1); // Exit if critical setup fails
}
// Register basic rate limiting
// Consider making max/timeWindow configurable via env vars
await fastify.register(fastifyRateLimit, {
max: 100, // Max requests per window per IP
timeWindow: '1 minute', // Time window
// keyGenerator: function (req) { /* more sophisticated key */ return req.ip }
});
// Register routes with a prefix
await fastify.register(smsRoutes, { prefix: '/api/v1' });
fastify.log.info('Application setup complete. Routes registered under /api/v1');
return fastify;
}
export default buildApp;Application configuration:
- Conditional logging with
pino-prettyfor development, JSON for production - Validates Vonage SDK initialization before starting server
- Implements rate limiting (100 requests per minute per IP)
- API versioning with
/api/v1prefix
Create Server Entry Point
Create server.js to start the Fastify application:
// server.js
import buildApp from './src/app.js';
async function start() {
let fastify;
try {
fastify = await buildApp();
// Use config loaded by the app
const port = fastify.config.PORT;
const host = fastify.config.HOST;
await fastify.listen({ port, host });
// Logger is available after listen resolves successfully
fastify.log.info(`Server listening on http://${host}:${port}`);
fastify.log.info(`SMS Send Endpoint: POST http://${host}:${port}/api/v1/send-sms`);
fastify.log.info(`Health Check Endpoint: GET http://${host}:${port}/api/v1/health`);
} catch (err) {
// Use console.error for reliable logging during startup failures
console.error('Error starting server:', err);
// Attempt to log with Fastify logger if available, otherwise console is fallback
if (fastify && fastify.log) {
fastify.log.error(err);
}
process.exit(1);
}
}
start();Startup features:
- Robust error handling for server initialization failures
- Clear logging of available endpoints
- Graceful failure with proper exit codes
Add NPM Scripts
Update the scripts section in your package.json:
{
"scripts": {
"start": "node server.js",
"dev": "NODE_ENV=development node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
}npm start: Production mode with JSON loggingnpm run dev: Development mode with pretty-printed logs
4. Testing Your SMS API
Now let's verify the SMS endpoint works correctly.
Verify Environment Configuration
Double-check that your .env file contains the correct Vonage credentials. If using a trial account, ensure the target phone number is added to your Test Numbers list in Vonage.
Start the Development Server
Launch the server in development mode:
npm run devExpected output:
INFO: Server listening on http://0.0.0.0:3000
INFO: SMS Send Endpoint: POST http://0.0.0.0:3000/api/v1/send-sms
INFO: Health Check Endpoint: GET http://0.0.0.0:3000/api/v1/health
Send a Test SMS Request
Open a new terminal window and send a test request using cURL. Replace +1YOUR_TEST_PHONE_NUMBER with a verified test number in E.164 format:
curl -X POST http://localhost:3000/api/v1/send-sms \
-H "Content-Type: application/json" \
-d '{
"to": "+1YOUR_TEST_PHONE_NUMBER",
"message": "Hello from Fastify and Vonage! Sent at: '"$(date)"'"
}'Verify the Response
Success response (HTTP 200):
{
"success": true,
"message_uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"message": "SMS submitted successfully to +1YOUR_TEST_PHONE_NUMBER"
}The SMS should arrive on the target phone within seconds. Check your server terminal for confirmation logs.
Error response example (HTTP 400):
{
"success": false,
"message": "Destination number not whitelisted for trial account."
}Check server logs for detailed error information if issues occur.
Test Input Validation
Verify that validation works correctly by testing invalid requests:
Missing required field:
curl -X POST http://localhost:3000/api/v1/send-sms -H "Content-Type: application/json" -d '{"message": "test"}'Expected: 400 Bad Request with validation error about missing to property.
Invalid phone format:
curl -X POST http://localhost:3000/api/v1/send-sms -H "Content-Type: application/json" -d '{"to": "12345abc", "message": "test"}'Expected: 400 Bad Request with pattern validation error.
Extra invalid property:
curl -X POST http://localhost:3000/api/v1/send-sms -H "Content-Type: application/json" -d '{"to": "+14155550100", "message": "test", "extraField": 123}'Expected: 400 Bad Request with additional properties error.
Test Health Endpoint
Verify the health check endpoint:
curl http://localhost:3000/api/v1/healthExpected response (HTTP 200):
{"status":"ok","timestamp":"2025-04-21T10:30:45.123Z"}5. Error Handling and Logging Best Practices
Proper error handling and logging are essential for maintaining a production SMS API.
Logging Implementation
The application uses Fastify's built-in Pino logger for structured logging:
- Development mode (
npm run dev): Usespino-prettyfor human-readable output - Production mode (
npm start): Outputs JSON format for log management systems - Logs include request information, successful sends, and detailed error context
Monitor logs in the terminal where the server is running to track all SMS operations.
Error Handling Strategy
Validation Errors:
- Fastify automatically handles request schema validation
- Returns detailed 400 Bad Request responses with specific validation failures
Vonage API Errors:
- The
sendSmsservice catches SDK errors and logs detailed information - Route handler attempts to return appropriate HTTP status codes:
- 400 for non-whitelisted numbers or invalid parameters
- 401 for authentication failures
- 500 for other server/API errors
Initialization Errors:
- Errors during Vonage SDK initialization cause the application to exit
- Prevents running in a broken state with invalid credentials
- Critical errors logged to console for visibility
Retry Mechanisms
For simple API-triggered SMS sending, retries are typically handled by the calling client. For production systems with asynchronous workflows (job queues, background processing), consider:
- Implementing retries with exponential backoff for transient errors
- Handling specific error types (429 Too Many Requests, temporary 5xx errors)
- Using libraries like
async-retrywithin thesendSmsfunction - Integrating with job queue systems for automatic retry logic
6. Security Features and Best Practices
Security is critical when building SMS APIs that handle user data and API credentials.
Input Validation
Implemented via Fastify's schema validation using @sinclair/typebox:
- Prevents malformed requests from reaching application logic
- Enforces E.164 phone number format
- Validates message content length
- Rejects unexpected fields with
additionalProperties: false
Rate Limiting
Basic IP-based rate limiting using fastify-rate-limit:
- Mitigates DoS attacks and accidental abuse
- Default: 100 requests per minute per IP address
- Configure
maxandtimeWindowbased on expected usage - Consider per-API-key rate limiting for authenticated APIs
- Integrate with API gateways for advanced protection in production
Secret Management
API credentials are managed securely via environment variables:
- Development:
.envfile (excluded from Git via.gitignore) - Production: Use platform-specific secret management:
- AWS Secrets Manager
- Google Secret Manager
- HashiCorp Vault
- Azure Key Vault
- Doppler or similar services
- Never hardcode credentials in source code
Private Key Handling
The service supports flexible private key management:
- File-based (development): Read from path specified in
VONAGE_PRIVATE_KEY_PATH - Inline content (production): Inject key directly into environment variable
- Ensure file permissions restrict access if using file-based approach
- Never commit private keys to version control
Additional Security Recommendations
Security Headers:
Install and configure @fastify/helmet:
npm install @fastify/helmetimport helmet from '@fastify/helmet';
await fastify.register(helmet);Authentication:
Protect endpoints using API keys, JWT tokens, or OAuth with @fastify/auth:
npm install @fastify/authDependency Security:
Regularly audit and update dependencies:
npm audit
npm audit fixFrequently Asked Questions
How do I send SMS with Fastify and Vonage?
Install the @vonage/server-sdk package, configure your Vonage Application ID and private key, then create a Fastify route that calls vonage.messages.send() with the recipient number, message content, and SMS channel configuration.
What phone number format does Vonage require?
Vonage requires E.164 format for all phone numbers: a plus sign (+) followed by the country code and number without spaces or special characters. Examples: +12125551234 (US), +442071838750 (UK).
How much does it cost to send SMS with Vonage?
Vonage SMS pricing varies by destination country and message type. US SMS typically starts around $0.0076 per message segment. Check current pricing at vonage.com/communications-apis/sms/pricing.
Can I use Vonage with a trial account?
Yes, Vonage offers free trial credit for new accounts. Trial accounts can only send SMS to verified numbers added to your Test Numbers list in the Vonage Dashboard under Numbers → Test numbers.
How do I handle SMS delivery failures?
Implement error handling in your route handler to catch Vonage API errors. Log detailed error information for debugging. For production systems, consider implementing retry logic with exponential backoff for transient failures.
What is the SMS character limit for Vonage?
Standard SMS supports up to 160 characters (GSM-7 encoding) or 70 characters (Unicode). Longer messages are automatically segmented, with each segment billed separately. The maximum total length is 1,600 characters.
How do I secure my Vonage API credentials?
Store credentials in environment variables using .env files for development. In production, use secure secret management services like AWS Secrets Manager, Google Secret Manager, or HashiCorp Vault. Never commit credentials to version control.
Can I send MMS with this setup?
Yes, the Vonage Messages API supports MMS. Modify the request to include image, audio, or video URLs in the message object. Check Vonage documentation for MMS-specific parameters and supported countries.
How do I monitor SMS delivery status?
Configure a Status URL webhook in your Vonage Application to receive delivery receipts. Vonage sends HTTP POST requests with delivery status updates (delivered, failed, etc.) to your webhook endpoint.
What Node.js version is required?
Use Node.js LTS version 18 or later for optimal compatibility with Fastify v5 and the latest @vonage/server-sdk features, including modern async/await patterns and security updates.
Related Resources
Learn more about building SMS applications with Node.js and Fastify:
- Vonage Messages API Documentation
- Fastify Official Documentation
- Node.js SMS Best Practices Guide
- E.164 Phone Number Format Explained
- Building SMS Marketing Campaigns with Fastify
- Schedule SMS with Vonage and Fastify
Next Steps: Now that you have a working SMS API, consider exploring advanced features like two-way messaging, delivery status webhooks, or bulk SMS campaigns. Check out our related tutorials for implementing these features with Fastify and Vonage.
Frequently Asked Questions
How to send SMS with Node.js and Fastify?
Use the Vonage Messages API and Fastify framework. Set up a Fastify server, integrate the Vonage Node.js SDK, and create a POST route to handle SMS sending requests. This setup allows your Node.js application to send SMS messages programmatically.
What is the Vonage Messages API used for?
The Vonage Messages API is a service for sending messages through various channels like SMS. It's used to reliably deliver transactional and notification messages within applications. The API handles the complexities of message delivery, letting developers focus on application logic.
Why use Fastify for SMS sending?
Fastify is a high-performance Node.js web framework known for speed and extensibility. Its efficient design minimizes overhead, making it ideal for handling API requests like those for sending SMS messages.
When should I use the Vonage Node.js SDK?
The Vonage Node.js SDK simplifies interactions with Vonage APIs. It handles authentication, request formatting, and response parsing. Use it whenever your Node.js application needs to communicate with Vonage services like the Messages API for SMS sending.
How to set up Vonage Messages API credentials?
Obtain API Key and Secret from your Vonage dashboard, create a Vonage Application, generate and secure a private key, and link a Vonage virtual number or an Alphanumeric Sender ID. Update all these environment variables in your project's .env file.
What is the role of fastify-env?
Fastify-env loads and validates environment variables, ensuring your application has the necessary configuration values. It helps manage sensitive data like API keys and secrets securely. Use it in combination with the dotenv package for loading environment variables from a .env file during development.
How to handle Vonage API errors?
Implement error handling within your Vonage service function to catch and log errors from the Vonage SDK. The route handler should also catch and re-throw errors with appropriate HTTP status codes (e.g., 400, 401, 500).
What security measures are implemented in the Fastify SMS sender?
The application includes schema validation with @sinclair/typebox to sanitize input, basic IP rate limiting using fastify-rate-limit to prevent abuse, and environment variable management for secure credential storage. These mechanisms mitigate common security risks and improve application robustness.
Can I use an Alphanumeric Sender ID with Vonage?
Yes, if it's approved for your account. Instead of a phone number, you can configure an Alphanumeric Sender ID (e.g., 'MyApp') as the 'from' address for your SMS messages in your .env file under VONAGE_SMS_FROM
How to structure a Fastify project for sending SMS?
Organize your code into directories for plugins (env.js), routes (sms.js), and services (vonage.js). This modular structure promotes clean separation of concerns and maintainability. Create app.js for Fastify setup and server.js for launching the application.
What are the prerequisites for this Node.js SMS project?
Ensure you have Node.js (LTS recommended), npm, a Vonage API account, and a basic understanding of Node.js, asynchronous programming, and REST APIs. A tool for making HTTP requests (like cURL or Postman) is essential for testing.
How does the SMS sending process work architecturally?
A client sends a POST request to the Fastify API endpoint. The server validates the request and uses the Vonage Node.js SDK to call the Vonage Messages API. Vonage delivers the SMS, returns a response to the server, which then sends a final response to the client.
How to test the Fastify SMS API endpoint?
Populate the .env file with your credentials and test numbers, then run npm run dev. Send requests using tools like cURL or Postman to the /api/v1/send-sms endpoint with valid parameters. Use the provided cURL examples in the tutorial to test the API.
Where do I find Vonage API credentials?
You can find your API Key, Secret, and Application settings in your Vonage API Dashboard. Navigate to the settings section to manage these settings. Ensure that 'Default SMS Setting' is set to 'Messages API'.