code examples
code examples
Send SMS messages with Node.js, Express, and Vonage
A guide to building a Node.js/Express app to send SMS using the Vonage API, covering setup, implementation, error handling, and security.
This guide provides a comprehensive walkthrough for building a production-ready Node.js application using the Express framework to send SMS messages via the Vonage API. We'll cover everything from initial project setup to deployment and verification, ensuring you have a robust foundation for integrating SMS capabilities into your applications.
By the end of this tutorial, you will have a simple REST API endpoint that accepts a phone number and a message, and uses the Vonage API to send an SMS.
Project Overview and Goals
What We're Building:
We will create a simple Node.js server using the Express framework. This server will expose a single API endpoint (POST /api/send-sms). When this endpoint receives a request containing a recipient phone number and a message body, it will use the Vonage Node.js SDK to send an SMS message to the specified recipient via the Vonage API (specifically, using the Messages API).
Problem Solved:
This guide addresses the common need for applications to send programmatic SMS messages for notifications, alerts, verification codes, or basic communication, providing a reliable and straightforward method using industry-standard tools.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications. Chosen for its performance, large ecosystem (npm), and asynchronous nature, suitable for I/O-bound tasks like API calls.
- Express: A minimal and flexible Node.js web application framework. Chosen for its simplicity, robustness, and widespread adoption for building APIs in Node.js.
- Vonage Messages API: A service enabling developers to send and receive messages across various channels (SMS, WhatsApp, etc.). Chosen for its unified API structure, developer support, and reliability. We will use it via the Node.js SDK to send SMS.
@vonage/server-sdk(v3+): The official Vonage Node.js library for interacting with Vonage APIs. Chosen for simplifying API calls, authentication, and response handling, using the modernmessages.send()method.dotenv: A module to load environment variables from a.envfile intoprocess.env. Chosen for securely managing API keys and configuration outside of the codebase.libphonenumber-js: A library for parsing, formatting, and validating phone numbers. Chosen to ensure robust handling of the recipient number format (E.164).
System Architecture:
The basic flow of information is as follows:
+--------+ +-------------------+ +---------------------+ +--------------+ +-----------+
| Client | ----> | Node.js/Express | ---->| Vonage SDK | ---->| Vonage API | ---->| Recipient |
| (e.g., | POST | API Server | | (@vonage/server-sdk)| | (Messages API| | Phone |
| curl, | Req | (localhost:3000) | | v3 - messages.send())| | SMS Gateway) | +-----------+
| App) +<---- +-------------------+ <----+ |<---- +--------------+
| JSON | | JSON | | API
| Resp | | Resp | | Resp
+--------+ +-------------------+ +---------------------+(Note: ASCII diagram rendering may vary depending on the platform and screen size. Image-based diagrams are recommended for complex architectures.)
Prerequisites:
- Node.js and npm (or yarn): Installed on your system. (Download Node.js)
- Vonage API Account: Sign up for a free account if you don't have one. (Vonage Signup)
- Vonage API Key and Secret: Found on your Vonage API Dashboard.
- Vonage Virtual Number: Rent a virtual number from the Vonage dashboard that is SMS-enabled. This will be your
fromnumber. - Test Recipient Phone Number(s): Crucially, if using a Vonage trial account, you must whitelist recipient numbers in your dashboard settings before you can send them SMS. This is a very common point of failure for new users.
- Basic understanding of JavaScript and Node.js.
- A tool for making HTTP requests: Such as
curl, Postman, or Insomnia.
1. Setting up the project
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir vonage-sms-guide cd vonage-sms-guide -
Initialize Node.js Project: Use npm to create a
package.jsonfile. The-yflag accepts the default settings.bashnpm init -y -
Install Dependencies: Install Express for the web server, the Vonage Server SDK,
dotenvfor environment variable management, andlibphonenumber-jsfor phone number validation.bashnpm install express @vonage/server-sdk dotenv libphonenumber-js -
Enable ES Modules & Configure
package.json: Open the generatedpackage.jsonfile. Add""type"": ""module""to enable ES Moduleimportsyntax. Update thescriptssection and review the dependencies.json// package.json { ""name"": ""vonage-sms-guide"", ""version"": ""1.0.0"", ""description"": ""Guide for sending SMS with Node.js, Express, and Vonage"", ""main"": ""index.js"", ""type"": ""module"", ""scripts"": { ""start"": ""node index.js"", ""test"": ""echo \""Error: no test specified\"" && exit 1"" }, ""keywords"": [ ""vonage"", ""sms"", ""nodejs"", ""express"" ], ""author"": """", ""license"": ""ISC"", ""dependencies"": { ""@vonage/server-sdk"": ""^3.10.0"", ""dotenv"": ""^16.3.1"", ""express"": ""^4.18.2"", ""libphonenumber-js"": ""^1.10.50"" } }Note: The versions shown (
^3.10.0,^16.3.1,^4.18.2,^1.10.50) are examples. Use the versions installed by npm or check for the latest compatible versions. -
Create Environment File: Create a file named
.envin the root of your project directory. This file will store your Vonage credentials securely. Never commit this file to version control.env# .env VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET VONAGE_VIRTUAL_NUMBER=YOUR_VONAGE_NUMBER_E164 PORT=3000Replace
YOUR_API_KEY,YOUR_API_SECRET, andYOUR_VONAGE_NUMBER_E164with your actual credentials from the Vonage dashboard. Use the E.164 format for the number (e.g.,+14155550100). -
Create
.gitignoreFile: Prevent sensitive files and unnecessary directories from being committed to Git. Create a.gitignorefile in the root directory:text# .gitignore node_modules .env npm-debug.log* yarn-debug.log* yarn-error.log* coverage *.log -
Project Structure: Your basic project structure should now look like this:
textvonage-sms-guide/ ├── .env ├── .gitignore ├── index.js (We will create this next) ├── node_modules/ ├── package.json └── package-lock.json(Note: ASCII diagram rendering may vary.)
2. Implementing core functionality
Now, let's write the core logic for initializing the server and the Vonage client.
-
Create
index.js: Create the main application file,index.js, in the project root. -
Import Dependencies and Configure: Add the following code to
index.jsto import necessary modules, load environment variables, and initialize the Express application and Vonage SDK.javascript// index.js import express from 'express'; import { Vonage } from '@vonage/server-sdk'; import { SMS } from '@vonage/messages'; // Import the SMS class specifically import 'dotenv/config'; // Loads .env file contents into process.env import { parsePhoneNumberFromString } from 'libphonenumber-js'; // --- Configuration --- const app = express(); const port = process.env.PORT || 3000; // Use port from .env or default to 3000 // --- Vonage Client Initialization --- // Ensure API Key and Secret are loaded if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET) { console.error('FATAL ERROR: VONAGE_API_KEY or VONAGE_API_SECRET not found in .env file.'); process.exit(1); // Exit if credentials are missing } if (!process.env.VONAGE_VIRTUAL_NUMBER) { console.error('FATAL ERROR: VONAGE_VIRTUAL_NUMBER not found in .env file.'); process.exit(1); // Exit if sender number is missing } const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET }); // --- Express Middleware --- // Enable Express to parse JSON request bodies app.use(express.json()); // Enable Express to parse URL-encoded request bodies app.use(express.urlencoded({ extended: true })); // --- Core SMS Sending Logic --- async function sendSms(toNumber, messageText) { const fromNumber = process.env.VONAGE_VIRTUAL_NUMBER; // Sender number from .env // Input validation for 'to' and 'text' should happen in the route handler before calling this. // We assume valid E.164 format for `toNumber` here. try { // Use the Messages API client from the initialized SDK const responseData = await vonage.messages.send( new SMS({ to: toNumber, from: fromNumber, text: messageText, }), ); console.log('Message sent successfully with UUID:', responseData.messageUuid); return responseData; // Return the success response data (contains message_uuid) } catch (err) { // Log the detailed error from the Vonage SDK console.error('Error sending SMS via Vonage Messages API:', err); // Extract meaningful error details if available (structure might vary) let errorMessage = 'Failed to send SMS.'; if (err.response?.data?.title) { errorMessage = `${err.response.data.title}: ${err.response.data.detail || 'No details provided.'}`; } else if (err.message) { errorMessage = err.message; } // Re-throw a new error with a potentially cleaner message for the API layer throw new Error(`Vonage API Error: ${errorMessage}`); } } // --- API Endpoint (Defined in next section) --- // app.post('/api/send-sms', ...); // --- Health Check Endpoint --- app.get('/health', (req, res) => { res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() }); }); // --- Start Server --- // Only start server if not in test environment if (process.env.NODE_ENV !== 'test') { app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); }); } // --- Export for potential testing --- export { app, sendSms }; // Export app for supertest
Explanation:
- We import
express,Vonage,SMSfrom@vonage/messages,dotenv/config, andlibphonenumber-js. - We initialize the Express
app. - We initialize the
Vonageclient using credentials loaded fromprocess.env, including fatal error checks. - We add Express middleware
express.json()andexpress.urlencoded(). - The
sendSmsasync function now usesvonage.messages.send(), passing an instance of theSMSclass which contains theto,from, andtextparameters.- It retrieves the
fromnumber (sender ID) from environment variables. - It calls the SDK method within a
try...catchblock. - Success: If the promise resolves, it logs the
messageUuidfrom the response and returns the response object. There's no need for status code checking within thetryblock; the SDK throws an error on failure. - Failure: The
catchblock logs the detailed error object from the SDK. It attempts to extract a user-friendly error message from the error response structure (err.response.data) common in Axios-based errors (which the SDK uses) or falls back toerr.message. It then throws a new, slightly formatted error to be caught by the API route handler.
- It retrieves the
- A simple
/healthendpoint is added for monitoring. app.listenstarts the server, wrapped in a check to prevent starting during tests.appandsendSmsare exported for use in testing frameworks like Jest/Supertest.
3. Building the API layer
Now, let's define the REST API endpoint that will trigger the SMS sending function.
-
Define the POST Endpoint: Add the following route handler in
index.jsbefore theapp.get('/health')andapp.listencalls.javascript// index.js (Add this section after middleware, before health check) // --- API Endpoint --- app.post('/api/send-sms', async (req, res) => { // 1. Basic Input Presence Check const { to, text } = req.body; // Destructure from JSON body if (!to || !text) { return res.status(400).json({ success: false, message: 'Missing required fields: ""to"" (recipient phone number) and ""text"" (message content).' }); } // 2. Robust Phone Number Validation (E.164) let e164Number; try { const phoneNumber = parsePhoneNumberFromString(to); // Attempt to parse using default country if needed if (!phoneNumber || !phoneNumber.isValid()) { return res.status(400).json({ success: false, message: 'Invalid phone number format provided for ""to"" field. Please use E.164 format (e.g., +15551234567).' }); } e164Number = phoneNumber.format('E.164'); // Get the standardized E.164 format } catch (parseError) { console.error(""Phone number parsing error:"", parseError); return res.status(400).json({ success: false, message: 'Error parsing phone number for ""to"" field.' }); } // ** Reminder for Trial Accounts ** // If using a Vonage trial account, ensure the 'to' number (e164Number) // has been added and verified in your Vonage dashboard under 'Test Numbers'. // Otherwise, the API call will fail. try { // 3. Call Core Logic with validated number const result = await sendSms(e164Number, text); // 4. Send Success Response return res.status(200).json({ success: true, message: 'SMS submitted successfully.', data: { message_uuid: result.messageUuid // Include Vonage message UUID } }); } catch (error) { // 5. Send Error Response console.error('API Error in /api/send-sms route:', error.message); // Log the specific error from sendSms // Determine status code based on error type if needed, default to 500 for server/Vonage issues return res.status(500).json({ success: false, // Use the message from the error thrown by sendSms message: error.message || 'Failed to send SMS due to an internal server error.' }); } }); // --- Health Check Endpoint --- // app.get('/health', ...); // Already defined earlier // --- Start Server --- // if (process.env.NODE_ENV !== 'test') { ... } // Already defined earlier
Explanation:
- Presence Validation: Checks if
toandtextexist inreq.body. - Phone Number Validation: Uses
libphonenumber-js'sparsePhoneNumberFromString.- It attempts to parse the input
tonumber. - If parsing fails or the number is invalid (
!phoneNumber.isValid()), it returns a400 Bad Request. - If valid, it formats the number into strict E.164 format (
e164Number).
- It attempts to parse the input
- Trial Account Reminder: A comment is added to remind developers testing the endpoint about the whitelisting requirement.
- Core Logic Call: Calls
sendSmswith the validatede164Numberandtext. - Success Response: If
sendSmscompletes, sends a200 OKwithsuccess: trueand themessage_uuidreturned by the Vonage Messages API. - Error Response: If
sendSmsthrows an error, thecatchblock sends a500 Internal Server Error(appropriate for upstream API failures or internal issues) containing the error message generated withinsendSms.
Testing the Endpoint:
You can test this endpoint using curl in your terminal (make sure the server is running: npm start).
- Replace
YOUR_RECIPIENT_NUMBER_E164with a valid phone number in E.164 format (e.g.,+15551234567). Remember to whitelist this number in your Vonage dashboard if using a trial account! - Replace
YOUR_MESSAGEwith the desired text.
curl -X POST http://localhost:3000/api/send-sms \
-H ""Content-Type: application/json"" \
-d '{
""to"": ""YOUR_RECIPIENT_NUMBER_E164"",
""text"": ""YOUR_MESSAGE""
}'Expected Success Response (JSON):
{
""success"": true,
""message"": ""SMS submitted successfully."",
""data"": {
""message_uuid"": ""xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx""
}
}(The UUID will be unique for each message)
Expected Error Response (JSON, e.g., invalid credentials detected by SDK):
{
""success"": false,
""message"": ""Vonage API Error: Authentication failed""
}(The exact error message depends on the failure reason)
Expected Error Response (JSON, e.g., invalid phone number format):
{
""success"": false,
""message"": ""Invalid phone number format provided for \""to\"" field. Please use E.164 format (e.g., +15551234567).""
}(No XML examples are provided as the API consumes and produces JSON.)
4. Integrating with Vonage
We've initialized the SDK, but let's detail how to get the necessary credentials and numbers.
- Sign Up/Log In: Go to the Vonage API Dashboard and log in or sign up.
- Find API Key and Secret:
- On the main dashboard page after logging in, your API key and API secret should be visible near the top.
- Navigation Path: Dashboard Home.
- Copy these values carefully.
- Get a Virtual Number (Sender ID):
- You need a Vonage number to send SMS messages from.
- Navigation Path: Numbers -> Buy numbers.
- Search for numbers using criteria like country and features (ensure SMS is selected).
- Purchase a number. The cost will be deducted from your account credit.
- Navigation Path: Numbers -> Your numbers.
- Find the number you just purchased (or an existing one). Copy the full number in E.164 format (e.g.,
+14155550100).
- Update
.envFile:- Open your
.envfile. - Paste the API Key into
VONAGE_API_KEY. - Paste the API Secret into
VONAGE_API_SECRET. - Paste the full virtual number in E.164 format (e.g.,
+14155550100) intoVONAGE_VIRTUAL_NUMBER. Consistency is key: Use the E.164 format (+prefix and country code) for both thefromnumber in your.envfile / API call and thetonumber provided in the request. While some older APIs or documentation might have shown variations, using E.164 for both is the clearest and generally recommended approach with the modern Messages API.
- Open your
Environment Variable Explanation:
VONAGE_API_KEY: Your public Vonage API key used for identification.VONAGE_API_SECRET: Your private Vonage API secret used for authentication. Treat this like a password.VONAGE_VIRTUAL_NUMBER: The Vonage phone number (purchased or assigned) in E.164 format that messages will be sent from. This must be SMS-capable.PORT: The network port your Express server will listen on.
Security: The .env file ensures your credentials are not hardcoded. The .gitignore entry prevents committing this file. When deploying, use your hosting provider's secure environment variable management system.
5. Error Handling and Logging
We have improved error handling, let's summarize and discuss enhancements.
Consistent Strategy:
- API Route Level: Use
try...catchin API route handlers (/api/send-sms) to catch errors from validation or the core logic. Format errors into consistent JSON responses ({ success: false, message: '...' }) with appropriate HTTP status codes (400 for bad input, 500 for server/API errors). - Core Logic Level: Use
try...catchinsendSmsto handle errors specifically from the Vonage SDK (vonage.messages.send). Log detailed errors server-side. Throw a new, potentially cleaner error upwards for the route handler to catch. - Input Validation: Perform thorough input validation (like the E.164 check) in the route handler before calling the core logic, returning 4xx errors immediately for invalid input.
Logging:
- We are using
console.logfor success information (message UUID) andconsole.errorfor detailed errors. - Production Logging: For production, replace
consolewith a dedicated logging library like Winston or Pino. Benefits include:- Log Levels: Control verbosity (debug, info, warn, error).
- Structured Logging (JSON): Easier parsing by log aggregation tools (Datadog, Splunk, ELK).
- Transports: Send logs to files, databases, or external services.
Example using Winston (Conceptual - requires npm install winston):
// Conceptual Winston Setup (e.g., in a logger.js file)
import winston from 'winston';
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }), // Log stack traces
winston.format.json()
),
defaultMeta: { service: 'sms-service' }, // Add service context
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
}));
}
export default logger;
// --- In index.js ---
// import logger from './logger.js'; // Assuming logger setup is in logger.js
// Replace console.log('Message sent...') with logger.info('SMS submitted successfully', { messageUuid: responseData.messageUuid });
// Replace console.error(...) with logger.error('Error sending SMS', { error: err }); // Pass the error objectRetry Mechanisms:
Network glitches or temporary Vonage issues can occur.
- Strategy: Wrap the
vonage.messages.sendcall with a retry library likeasync-retryorp-retry. - Configuration: Use exponential backoff (increasing delays) and limit the number of retries.
- Idempotency: The Messages API uses unique UUIDs, but retrying a request that succeeded but whose response was lost could potentially lead to duplicate messages if not handled carefully (though Vonage might have some internal checks). Consider if duplicates are acceptable for your use case. For critical sends, you might need a more complex system involving storing message state.
Testing Error Scenarios:
- Provide invalid/missing API credentials in
.env. - Send requests with invalid phone number formats to
/api/send-sms. - Send requests to a non-whitelisted number (trial account).
- Send requests missing
toortextfields. - Temporarily disable network connectivity (if possible) or mock network errors in tests.
- Monitor logs (
consoleor your logging service) during tests to observe error messages.
6. Database Schema and Data Layer
Not Applicable: This guide focuses purely on the stateless action of sending an SMS via an API call. No persistent data storage (database) is needed for this core functionality.
If Extending: If you needed to track sent messages, manage contacts, schedule SMS, etc., you would introduce a database:
- Potential Schema:
messagestable (id,message_uuid,vonage_status,to_number,from_number,message_text,submitted_at,cost,error_message). - Data Layer: Use an ORM (e.g., Prisma, Sequelize) or a query builder (Knex.js) to interact with your chosen database (PostgreSQL, MySQL, MongoDB, etc.).
- Migrations: Manage database schema changes using tools provided by the ORM or dedicated migration libraries.
7. Security Features
Enhance the security of your API:
-
Input Validation and Sanitization:
- Implemented: Basic presence checks and robust E.164 phone number validation using
libphonenumber-jsare included in the API route. - Further Steps:
- Text Length: Consider adding validation for SMS text length if you need to enforce limits or inform users about potential multi-segment costs. Vonage handles segmentation, but costs vary.
- Sanitization: While less critical for E.164 numbers and plain text messages compared to HTML/SQL inputs, ensure no unexpected characters could cause issues.
libphonenumber-jshandles number formatting safely.
- Implemented: Basic presence checks and robust E.164 phone number validation using
-
Rate Limiting:
- Protect against abuse and control costs.
- Use middleware like
express-rate-limit.
Implementation:
bashnpm install express-rate-limitjavascript// index.js import rateLimit from 'express-rate-limit'; // ... other imports const app = express(); // ... other middleware (json, urlencoded) // Apply rate limiting specifically to the SMS sending endpoint const smsLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs standardHeaders: true, // Return rate limit info in `RateLimit-*` headers legacyHeaders: false, // Disable `X-RateLimit-*` headers message: { success: false, message: 'Too many SMS requests from this IP, please try again after 15 minutes.' }, // keyGenerator: (req, res) => req.ip // Default key is IP address }); // Apply the limiter middleware BEFORE the route handler app.post('/api/send-sms', smsLimiter, async (req, res) => { // ... existing route handler logic ... }); // ... rest of the file (health check, etc.)Adjust
windowMsandmaxaccording to your needs. -
Authentication/Authorization:
- Problem: The current
/api/send-smsendpoint is open to anyone who can reach it. - Solution (for real applications): Protect the endpoint. Options include:
- API Keys: Require clients to send a secret API key in headers. Validate it on the server.
- JWT (JSON Web Tokens): Issue tokens to authenticated users/services. Validate the token on each request.
- OAuth: For third-party integrations.
- Session Cookies: For browser-based applications.
- Use libraries like Passport.js to implement authentication strategies.
- Problem: The current
-
Security Headers:
- Use middleware like
helmetto set various HTTP headers that help protect against common web vulnerabilities (XSS, clickjacking, etc.). While less critical for a pure backend API, it's good practice.
bashnpm install helmetjavascript// index.js import helmet from 'helmet'; // ... const app = express(); app.use(helmet()); // Add Helmet middleware early, before other routes/middleware // ... rest of middleware (json, urlencoded, rate limiter) and routes - Use middleware like
-
Dependency Management: Keep dependencies updated (
npm audit, dependabot) to patch known vulnerabilities.
8. Handling Special Cases
SMS involves various edge cases:
-
Phone Number Formatting (E.164):
- Handled: We integrated
libphonenumber-jsin Section 3 to parse various input formats and strictly validate/convert thetonumber to E.164 (+15551234567) before sending it to thesendSmsfunction and Vonage. Thefromnumber is also expected in E.164 format from the.envfile.
- Handled: We integrated
-
SMS Character Limits & Encoding:
- Standard (GSM-7): ~160 characters per segment.
- Unicode (UCS-2): Used for emojis or non-Latin characters, ~70 characters per segment.
- Concatenation: Vonage automatically splits longer messages into multiple segments and attempts delivery as a single message on the handset.
- Impact: Each segment is billed separately. Be aware of costs for long messages or those using Unicode characters. You might add client-side or server-side warnings about length.
-
International Sending:
- Regulations: Sender ID rules (numeric vs. alphanumeric), opt-in requirements, and content restrictions vary significantly by country. Some countries block unregistered alphanumeric sender IDs or require pre-registration.
- Costs: Vary by destination country.
- Delivery: Can be affected by local carrier filtering or network issues.
- Action: Consult Vonage's country-specific documentation and ensure your account is enabled for desired destinations. Always use E.164 for international numbers.
-
Trial Account Restrictions:
- Crucial Point: As emphasized previously, new Vonage accounts require whitelisting recipient numbers under Dashboard -> Settings -> Test Numbers. Sending to non-whitelisted numbers will fail.
- Solution: Add/verify test numbers or upgrade your account by adding payment details.
-
Sender ID (From Number):
- Using a Vonage virtual number (as configured) is reliable.
- Alphanumeric Sender IDs: (e.g., ""MyCompany"") are possible but subject to country regulations and potential blocking/filtering. They cannot receive replies. Check Vonage docs for support and registration requirements per country.
9. Implementing Performance Optimizations
For this specific API, performance is mainly tied to the external Vonage API call, but consider:
-
SDK Initialization: The
VonageSDK client is initialized once when the server starts (outside the request handler), which is correct. Avoid re-initializing it on every request. -
Asynchronous Operations: Using
async/awaitcorrectly ensures Node.js's event loop is not blocked during the I/O-bound Vonage API call, allowing the server to handle concurrent requests efficiently. -
Payload Size: Keep request/response payloads reasonably small. Our current payloads are minimal.
-
Load Testing: Use tools like
k6,artillery, or ApacheBench (ab) to simulate traffic and measure performance (latency, requests per second, error rates) under load. Identify bottlenecks.Example
k6Script (requiresk6installation):javascript// load-test.js import http from 'k6/http'; import { check, sleep } from 'k6'; import { SharedArray } from 'k6/data'; // Ensure this number is whitelisted in your Vonage trial account! const testPhoneNumber = '+15551234567'; // CHANGE TO YOUR WHITELISTED NUMBER export const options = { vus: 10, // Simulate 10 concurrent virtual users duration: '30s', // Run test for 30 seconds thresholds: { // Define pass/fail criteria http_req_failed: ['rate<0.01'], // http errors should be less than 1% http_req_duration: ['p(95)<500'], // 95% of requests should be below 500ms }, }; export default function () { const url = 'http://localhost:3000/api/send-sms'; // Target your running local server const payload = JSON.stringify({ to: testPhoneNumber, text: `K6 load test message - VU: ${__VU} ITER: ${__ITER}`, // Unique text per request }); const params = { headers: { 'Content-Type': 'application/json', }, }; const res = http.post(url, payload, params); // Check if the request was successful (status 200) check(res, { 'status is 200': (r) => r.status === 200, 'response has success:true': (r) => r.json('success') === true, 'response has message_uuid': (r) => typeof r.json('data.message_uuid') === 'string', }); // Add a short sleep between requests to simulate realistic user behavior sleep(1); }
Frequently Asked Questions
how to send sms with node js and express
Use the Vonage Messages API with the Node.js Server SDK and Express. Set up a simple Express server with a POST endpoint that takes a phone number and message, then integrates the Vonage SDK to send the SMS through the Messages API.
what is vonage messages api and how to use it
The Vonage Messages API is a service for sending messages across multiple channels like SMS and WhatsApp. To use it, initialize the Vonage Node.js SDK with your API credentials, then use the `messages.send()` method to send SMS messages by providing the recipient number, your Vonage virtual number, and the message content within an instance of the `SMS` class from `@vonage/messages`.
why use express framework for node js sms app
Express.js simplifies building robust and scalable APIs in Node.js. Its middleware support helps handle JSON parsing, URL encoding, rate limiting, and security headers, making it well-suited for our SMS sending application.
when should I whitelist recipient numbers in vonage
You must whitelist recipient numbers in your Vonage dashboard *before* sending test SMS messages if you're using a trial account. This is a common requirement for testing and ensures that you can successfully send messages during development.
can I send sms to international numbers with vonage
Yes, Vonage supports international SMS. Ensure your account is enabled for international sending, consult Vonage's documentation for country-specific regulations, and always use the E.164 phone number format for international numbers.
what is the E.164 format for phone numbers
The E.164 format is an international standard for phone numbers. It includes a '+' sign followed by the country code and the national subscriber number without any spaces or special characters (e.g., +15551234567). Using E.164 ensures consistent number handling.
how to handle sms character limits with vonage api
Vonage automatically handles SMS character limits and message segmentation. Standard GSM-7 encoding allows ~160 characters/segment, while Unicode (for emojis) allows ~70. Longer messages are split into segments and billed accordingly.
how to secure my vonage sms api endpoint
Secure your endpoint using techniques like API keys, JWT for authentication, implementing rate limiting with `express-rate-limit`, and adding security headers with Helmet to protect against common web vulnerabilities. Validate inputs thoroughly using libraries like `libphonenumber-js`.
what is dotenv and why use it
Dotenv is a Node.js module that loads environment variables from a `.env` file into `process.env`. This securely manages credentials (API keys, secrets) without hardcoding them, improving code security. Remember never to commit .env files to version control!
how to manage vonage api credentials securely
Store your Vonage API Key and Secret in a `.env` file and load them using `dotenv`. Ensure the `.env` file is added to your `.gitignore` file to avoid committing it to version control. For production, use your platform's secure environment variable system.
how to install vonage server sdk for node js
Open your terminal, navigate to your project directory, and use the command `npm install @vonage/server-sdk` to install the Vonage Server SDK. The article recommends using version 3 or later.
why does node.js suit i/o bound tasks like api calls
Node.js excels at I/O-bound operations due to its non-blocking, event-driven architecture. While a request is waiting for a response from the Vonage API, Node.js can process other requests, making it efficient for handling API calls.
what is libphonenumber-js and why use it
`libphonenumber-js` is a library for parsing, formatting, and validating international phone numbers. It ensures you handle user-provided numbers correctly, converting them to the E.164 format required by Vonage and preventing errors.