code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

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 modern messages.send() method.
  • dotenv: A module to load environment variables from a .env file into process.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:

text
+--------+       +-------------------+      +---------------------+      +--------------+      +-----------+
| 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.

  1. Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.

    bash
    mkdir vonage-sms-guide
    cd vonage-sms-guide
  2. Initialize Node.js Project: Use npm to create a package.json file. The -y flag accepts the default settings.

    bash
    npm init -y
  3. Install Dependencies: Install Express for the web server, the Vonage Server SDK, dotenv for environment variable management, and libphonenumber-js for phone number validation.

    bash
    npm install express @vonage/server-sdk dotenv libphonenumber-js
  4. Enable ES Modules & Configure package.json: Open the generated package.json file. Add ""type"": ""module"" to enable ES Module import syntax. Update the scripts section 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.

  5. 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
    # .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, and YOUR_VONAGE_NUMBER_E164 with your actual credentials from the Vonage dashboard. Use the E.164 format for the number (e.g., +14155550100).

  6. Create .gitignore File: Prevent sensitive files and unnecessary directories from being committed to Git. Create a .gitignore file in the root directory:

    text
    # .gitignore
    node_modules
    .env
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log*
    coverage
    *.log
  7. Project Structure: Your basic project structure should now look like this:

    text
    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.

  1. Create index.js: Create the main application file, index.js, in the project root.

  2. 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.

    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, SMS from @vonage/messages, dotenv/config, and libphonenumber-js.
  • We initialize the Express app.
  • We initialize the Vonage client using credentials loaded from process.env, including fatal error checks.
  • We add Express middleware express.json() and express.urlencoded().
  • The sendSms async function now uses vonage.messages.send(), passing an instance of the SMS class which contains the to, from, and text 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 the try 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 to err.message. It then throws a new, slightly formatted error to be caught by the API route handler.
  • A simple /health endpoint is added for monitoring.
  • app.listen starts the server, wrapped in a check to prevent starting during tests.
  • app and sendSms 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.

  1. Define the POST Endpoint: Add the following route handler in index.js before the app.get('/health') and app.listen calls.

    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:

  1. Presence Validation: Checks if to and text exist in req.body.
  2. Phone Number Validation: Uses libphonenumber-js's parsePhoneNumberFromString.
    • It attempts to parse the input to number.
    • If parsing fails or the number is invalid (!phoneNumber.isValid()), it returns a 400 Bad Request.
    • If valid, it formats the number into strict E.164 format (e164Number).
  3. Trial Account Reminder: A comment is added to remind developers testing the endpoint about the whitelisting requirement.
  4. Core Logic Call: Calls sendSms with the validated e164Number and text.
  5. Success Response: If sendSms completes, sends a 200 OK with success: true and the message_uuid returned by the Vonage Messages API.
  6. Error Response: If sendSms throws an error, the catch block sends a 500 Internal Server Error (appropriate for upstream API failures or internal issues) containing the error message generated within sendSms.

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.
bash
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):

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):

json
{
  ""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):

json
{
    ""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.

  1. Sign Up/Log In: Go to the Vonage API Dashboard and log in or sign up.
  2. 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.
  3. 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).
  4. 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) into VONAGE_VIRTUAL_NUMBER. Consistency is key: Use the E.164 format (+ prefix and country code) for both the from number in your .env file / API call and the to 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.

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 in sendSms 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) and console.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):

javascript
// 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 like async-retry or p-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 or text 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:

  1. 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.
  2. Rate Limiting:

    Implementation:

    bash
    npm install express-rate-limit
    javascript
    // 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 and max according to your needs.

  3. 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.
  4. 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.
    bash
    npm install helmet
    javascript
    // 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
  5. Dependency Management: Keep dependencies updated (npm audit, dependabot) to patch known vulnerabilities.

8. Handling Special Cases

SMS involves various edge cases:

  1. Phone Number Formatting (E.164):

    • Handled: We integrated libphonenumber-js in Section 3 to parse various input formats and strictly validate/convert the to number to E.164 (+15551234567) before sending it to the sendSms function and Vonage. The from number is also expected in E.164 format from the .env file.
  2. 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.
  3. 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.
  4. 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.
  5. 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:

  1. 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.

  2. 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.

  3. Payload Size: Keep request/response payloads reasonably small. Our current payloads are minimal.

  4. 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 (requires k6 installation):

    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.