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.env
file 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
from
number. - 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.
mkdir vonage-sms-guide cd vonage-sms-guide
-
Initialize Node.js Project: Use npm to create a
package.json
file. The-y
flag accepts the default settings.npm init -y
-
Install Dependencies: Install Express for the web server_ the Vonage Server SDK_
dotenv
for environment variable management_ andlibphonenumber-js
for phone number validation.npm install express @vonage/server-sdk dotenv libphonenumber-js
-
Enable ES Modules & Configure
package.json
: Open the generatedpackage.json
file. Add""type"": ""module""
to enable ES Moduleimport
syntax. Update thescripts
section and review the dependencies.// 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
.env
in the root of your project directory. This file will store your Vonage credentials securely. Never commit this file to version control.# .env VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET VONAGE_VIRTUAL_NUMBER=YOUR_VONAGE_NUMBER_E164 PORT=3000
Replace
YOUR_API_KEY
_YOUR_API_SECRET
_ andYOUR_VONAGE_NUMBER_E164
with your actual credentials from the Vonage dashboard. Use the E.164 format for the number (e.g._+14155550100
). -
Create
.gitignore
File: Prevent sensitive files and unnecessary directories from being committed to Git. Create a.gitignore
file in the root directory:# .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:
vonage-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.js
to import necessary modules_ load environment variables_ and initialize the Express application and Vonage SDK.// 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
,SMS
from@vonage/messages
,dotenv/config
, andlibphonenumber-js
. - We initialize the Express
app
. - We initialize the
Vonage
client using credentials loaded fromprocess.env
, including fatal error checks. - We add Express middleware
express.json()
andexpress.urlencoded()
. - The
sendSms
async function now usesvonage.messages.send()
, passing an instance of theSMS
class which contains theto
,from
, andtext
parameters.- It retrieves the
from
number (sender ID) from environment variables. - It calls the SDK method within a
try...catch
block. - Success: If the promise resolves, it logs the
messageUuid
from the response and returns the response object. There's no need for status code checking within thetry
block; the SDK throws an error on failure. - Failure: The
catch
block 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
/health
endpoint is added for monitoring. app.listen
starts the server, wrapped in a check to prevent starting during tests.app
andsendSms
are 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.js
before theapp.get('/health')
andapp.listen
calls.// 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
to
andtext
exist inreq.body
. - Phone Number Validation: Uses
libphonenumber-js
'sparsePhoneNumberFromString
.- It attempts to parse the input
to
number. - 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
sendSms
with the validatede164Number
andtext
. - Success Response: If
sendSms
completes, sends a200 OK
withsuccess: true
and themessage_uuid
returned by the Vonage Messages API. - Error Response: If
sendSms
throws an error, thecatch
block 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_E164
with 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_MESSAGE
with 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
.env
File:- Open your
.env
file. - 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 thefrom
number in your.env
file / API call and theto
number 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...catch
in 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...catch
insendSms
to 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.log
for success information (message UUID) andconsole.error
for detailed errors. - Production Logging: For production, replace
console
with 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 object
Retry Mechanisms:
Network glitches or temporary Vonage issues can occur.
- Strategy: Wrap the
vonage.messages.send
call with a retry library likeasync-retry
orp-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
to
ortext
fields. - Temporarily disable network connectivity (if possible) or mock network errors in tests.
- Monitor logs (
console
or 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:
messages
table (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-js
are 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-js
handles 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:
npm install express-rate-limit
// 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
windowMs
andmax
according to your needs. -
Authentication/Authorization:
- Problem: The current
/api/send-sms
endpoint 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
helmet
to 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.
npm install helmet
// 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-js
in Section 3 to parse various input formats and strictly validate/convert theto
number to E.164 (+15551234567
) before sending it to thesendSms
function and Vonage. Thefrom
number is also expected in E.164 format from the.env
file.
- 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
Vonage
SDK 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/await
correctly 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
k6
Script (requiresk6
installation):// 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); }