code examples
code examples
Developer Guide: Sending SMS with Node.js, Express, and Vonage
A complete guide to building a Node.js/Express application for sending SMS messages using the Vonage Messages API, covering setup, implementation, error handling, and configuration.
This guide provides a complete walkthrough for building a Node.js application using the Express framework to send SMS messages via the Vonage Messages API. We will cover everything from project setup to deployment considerations, enabling you to integrate SMS functionality into your applications effectively.
This tutorial focuses on sending SMS messages. While Vonage also enables receiving messages via webhooks, that functionality requires a publicly accessible server (often set up using tools like ngrok during development) and is detailed in separate Vonage documentation.
Project Overview and Goals
Goal: To create a simple, robust Node.js API endpoint that accepts a recipient phone number and a message text, then uses the Vonage Messages API to send an SMS message.
Problem Solved: This provides a foundational service for applications needing to send programmatic SMS notifications, alerts, verification codes, or other messages to users.
Technologies Used:
- Node.js: A JavaScript runtime environment ideal for building scalable network applications.
- Express: A minimal and flexible Node.js web application framework providing features for web and mobile applications, perfect for creating our API endpoint.
- Vonage Messages API: A powerful API from Vonage that enables sending messages across various channels, including SMS, MMS, WhatsApp, and more. We'll use it specifically for SMS.
@vonage/server-sdk: The official Vonage Server SDK for Node.js, simplifying interactions with the Vonage APIs.dotenv: A zero-dependency module that loads environment variables from a.envfile intoprocess.env, crucial for managing sensitive credentials securely.
System Architecture:
graph LR
Client[Client Application / Postman / curl] -->|1. POST /send-sms (to, text)| ExpressAPI[Node.js/Express API Server];
ExpressAPI -->|2. Initialize Vonage SDK| VonageSDK[@vonage/server-sdk];
ExpressAPI -->|3. Call vonage.messages.send()| VonageSDK;
VonageSDK -->|4. Send SMS Request (HTTPS)| VonageAPI[Vonage Messages API];
VonageAPI -->|5. Process & Send SMS| SMSNetwork[Carrier SMS Network];
SMSNetwork -->|6. Deliver SMS| UserPhone[User's Phone];
VonageAPI -->|7. Return message_uuid (async)| VonageSDK;
VonageSDK -->|8. Return Response| ExpressAPI;
ExpressAPI -->|9. Return API Response (e.g., { success: true, messageId: ... })| Client;
subgraph Your Application
ExpressAPI
VonageSDK
end
subgraph Vonage Platform
VonageAPI
endPrerequisites:
- Node.js and npm: Installed on your system (LTS version recommended). Download from nodejs.org.
- Vonage API Account: Sign up for free at Vonage API Dashboard.
- Vonage Virtual Phone Number: You need a Vonage number capable of sending SMS messages. You can rent one via the Vonage Dashboard. Note that trial accounts may have restrictions (see Troubleshooting section).
- Vonage Application: A Vonage Application configured for the Messages API, with generated public/private keys.
- Basic Command Line/Terminal Knowledge: Familiarity with navigating directories and running commands.
- (Optional) API Testing Tool: Like Postman or
curlfor testing the API endpoint.
Final Outcome: A running Node.js server with a single API endpoint (/send-sms) that successfully sends an SMS message via Vonage when called with a valid phone number and message.
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for your project, then navigate into it.
bashmkdir vonage-sms-sender cd vonage-sms-sender -
Initialize Node.js Project: Use
npm initto create apackage.jsonfile. The-yflag accepts default settings.bashnpm init -y -
Install Dependencies: Install Express (web framework), the Vonage Server SDK, and
dotenv(for environment variables).bashnpm install express @vonage/server-sdk dotenv --saveexpress: Handles HTTP requests and routing.@vonage/server-sdk: Interacts with the Vonage API.dotenv: Loads environment variables from a.envfile.--save: Adds these packages to thedependenciessection in yourpackage.json.
-
Create Project Structure: Create the main application file and the environment file.
bashtouch index.js .env .gitignoreindex.js: Will contain our Express server and API logic..env: Will store our sensitive credentials (API keys, secrets, phone numbers). Never commit this file to version control..gitignore: Specifies intentionally untracked files that Git should ignore.
-
Configure
.gitignore: Addnode_modulesand.envto your.gitignorefile to prevent committing them.text# .gitignore node_modules/ .env *.log private.key # If storing the key directly in the project -
Configure
.env: Open the.envfile and add the following placeholders. We will fill these in later (Section 4).dotenv# .env PORT=3000 # Vonage Credentials (Using Application ID & Private Key - Recommended for Messages API) VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Or the full path to your key file # Vonage Virtual Number VONAGE_VIRTUAL_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER_E164PORT: The port number our Express server will listen on.VONAGE_APPLICATION_ID: Your Vonage Application ID (obtained from the Vonage Dashboard).VONAGE_PRIVATE_KEY_PATH: The file path to theprivate.keyfile downloaded when creating your Vonage Application. We assume it will be placed in the project root for this example.VONAGE_VIRTUAL_NUMBER: The Vonage phone number you rented, in E.164 format (e.g.,+12015550123).- Note on Authentication: The Messages API primarily uses Application ID and Private Key for authentication via JWTs generated by the SDK. This method is generally preferred for the Messages API and provides better security partitioning. We will use Application ID/Private Key in this guide.
2. Implementing Core Functionality
Now, let's write the core logic in index.js to initialize the Vonage SDK and prepare the function to send SMS.
// index.js
'use strict';
// 1. Load Environment Variables
require('dotenv').config(); // Load variables from .env file into process.env
// 2. Import Dependencies
const express = require('express');
const { Vonage } = require('@vonage/server-sdk'); // Import Vonage SDK
// 3. Initialize Vonage SDK
// Ensure required environment variables are present
if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_PRIVATE_KEY_PATH || !process.env.VONAGE_VIRTUAL_NUMBER) {
console.error(""Error: Required Vonage environment variables (VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH, VONAGE_VIRTUAL_NUMBER) are not set."");
console.error(""Please check your .env file."");
process.exit(1); // Exit if essential config is missing
}
const vonage = new Vonage({
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_PRIVATE_KEY_PATH,
});
// 4. Define the SMS Sending Function
async function sendSms(toNumber, messageText) {
// Validate input format (basic)
if (!/^\+[1-9]\d{1,14}$/.test(toNumber)) {
// Use standard quotes within the error string
throw new Error('Invalid ""to"" number format. Must be E.164 format (e.g., +12125551234)');
}
if (!messageText || typeof messageText !== 'string' || messageText.trim().length === 0) {
throw new Error('Invalid ""text"" parameter. Message text cannot be empty.');
}
const fromNumber = process.env.VONAGE_VIRTUAL_NUMBER;
// Use template literal for cleaner logging
console.log(`Attempting to send SMS from ${fromNumber} to ${toNumber}`);
try {
// Use vonage.messages.send() for the Messages API
const resp = await vonage.messages.send({
channel: 'sms',
message_type: 'text',
to: toNumber,
from: fromNumber,
text: messageText,
// Optional: client_ref for your internal tracking (max 40 chars)
// client_ref: `my-internal-ref-${Date.now()}`
});
console.log('SMS submitted successfully:');
console.log(resp); // Log the full response from Vonage
// The Messages API returns a message_uuid for tracking
if (resp && resp.message_uuid) {
return resp.message_uuid;
} else {
// This case might indicate an issue before actual sending attempt
console.error(""Unexpected response structure from Vonage:"", resp);
throw new Error('SMS submission failed or response format unexpected.');
}
} catch (err) {
console.error(""Error sending SMS via Vonage:"");
// Log detailed error if available (e.g., network issue, SDK error)
if (err.response && err.response.data) {
console.error(""Vonage API Error Details:"", JSON.stringify(err.response.data, null, 2));
// Rethrow a more specific error based on Vonage response if needed
throw new Error(`Vonage API Error: ${err.response.data.title || 'Unknown error'} - ${err.response.data.detail || JSON.stringify(err.response.data)}`);
} else {
console.error(err); // Log the raw error object
throw new Error(`Failed to send SMS: ${err.message || 'Unknown error'}`);
}
}
}
// --- Express API Setup will go here in the next section ---
// Placeholder for exports and starting the server (will be added later)Explanation:
- Load Environment Variables:
require('dotenv').config()reads the.envfile and makes variables likeprocess.env.VONAGE_APPLICATION_IDavailable. This should be done at the very beginning. - Import Dependencies: We import
expressand theVonageclass from the SDK. - Initialize Vonage SDK: We create a
Vonageinstance, passing our Application ID and the path to the private key file retrieved fromprocess.env. Basic validation ensures these crucial variables are set. - Define
sendSmsFunction:- This
asyncfunction takes the recipient number (toNumber) and the message (messageText) as arguments. - It performs basic validation on the
toNumber(checking for E.164 format) andmessageText. - It retrieves the
fromNumberfrom the environment variable. - It calls
vonage.messages.send(), specifying thechannel('sms'),message_type('text'),to,from, andtext. - The
try...catchblock handles potential errors during the API call (network issues, invalid credentials, Vonage API errors). - On success, it logs the response and returns the
message_uuid, which is Vonage's identifier for the submitted message. - On failure, it logs detailed error information and throws an error to be handled by the calling code (our API endpoint).
- This
3. Building a Complete API Layer
Now, let's use Express to create an API endpoint that utilizes our sendSms function.
Add the following code to the end of your index.js file:
// index.js (continued)
// --- Express API Setup ---
const app = express();
// Middleware to parse JSON request bodies
app.use(express.json());
// Middleware to parse URL-encoded request bodies
app.use(express.urlencoded({ extended: true }));
// Define the POST endpoint for sending SMS
app.post('/send-sms', async (req, res) => {
// Extract 'to' and 'text' from the request body
const { to, text } = req.body;
// Basic input validation for presence
if (!to || !text) {
// Corrected console error logging
console.error('Validation Error: Missing ""to"" or ""text"" in request body');
return res.status(400).json({
success: false,
// Corrected JSON string format
message: 'Missing required parameters: ""to"" (E.164 format) and ""text"".'
});
}
try {
// Call our core SMS sending function
const messageId = await sendSms(to, text);
// Send success response
console.log(`API successfully processed SMS request. Vonage Message ID: ${messageId}`);
return res.status(200).json({
success: true,
message: 'SMS submitted successfully.',
messageId: messageId // Include the Vonage message_uuid
});
} catch (error) {
// Handle errors from the sendSms function
console.error(`API Error processing /send-sms: ${error.message}`);
// Determine appropriate status code (e.g., 400 for validation, 500 for server/Vonage errors)
const statusCode = error.message.includes('Invalid') ? 400 : 500;
return res.status(statusCode).json({
success: false,
message: `Failed to send SMS: ${error.message}`
});
}
});
// Health check endpoint (Good practice)
app.get('/health', (req, res) => {
res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Export app and sendSms for testing purposes
// Note: Starting the server should typically be in a separate file (e.g., server.js)
// for better testability, but we'll keep it here for simplicity in this guide.
module.exports = { app, sendSms };
// Start the Express server (only if this file is run directly)
if (require.main === module) {
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
console.log(`SMS Send Endpoint: POST http://localhost:${PORT}/send-sms`);
console.log(`Health Check: GET http://localhost:${PORT}/health`);
});
}Explanation:
- Initialize Express:
const app = express();creates an Express application. - Middleware:
app.use(express.json());enables the server to parse incoming request bodies formatted as JSON.app.use(express.urlencoded({ extended: true }));enables parsing of URL-encoded data (often used by HTML forms).
- POST
/send-smsEndpoint:- Defines a route that listens for POST requests at the
/send-smspath. - It extracts the
tophone number andtextmessage from the request body (req.body). - It performs basic validation to ensure both parameters are present.
- It calls the
await sendSms(to, text)function within atry...catchblock. - On success, it returns a
200 OKresponse withsuccess: trueand themessageIdreceived from Vonage. - On failure (catching the error thrown by
sendSms), it logs the error and returns an appropriate error status code (400for bad input,500for internal/Vonage errors) and a JSON response withsuccess: falseand the error message.
- Defines a route that listens for POST requests at the
- GET
/healthEndpoint: A simple health check endpoint that returns a200 OKstatus, useful for monitoring. - Exports:
module.exports = { app, sendSms };exports the Expressappinstance and thesendSmsfunction, making them accessible for testing (See Section 13 - Note: Section 13 was mentioned but not provided in the original input, this reference is kept for context). - Start Server: The
if (require.main === module)block ensuresapp.listen()is only called whenindex.jsis run directly (e.g.,node index.js), not when it's required by another module (like a test file). This starts the server on the specifiedPORT.
Testing the Endpoint:
Once your Vonage credentials are set up (Section 4), you can test this endpoint using curl or Postman.
Using curl:
curl -X POST http://localhost:3000/send-sms \
-H ""Content-Type: application/json"" \
-d '{
""to"": ""+12015550124"",
""text"": ""Hello from Node.js and Vonage!""
}'Expected Success Response (JSON):
{
""success"": true,
""message"": ""SMS submitted successfully."",
""messageId"": ""aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee""
}Expected Error Response (JSON - e.g., missing parameter):
{
""success"": false,
""message"": ""Missing required parameters: \""to\"" (E.164 format) and \""text\"".""
}Expected Error Response (JSON - e.g., Vonage API failure):
{
""success"": false,
""message"": ""Failed to send SMS: Vonage API Error: Unauthorized - Please check your Application ID/private key""
}4. Integrating with Necessary Third-Party Services (Vonage)
This section details how to get the required credentials from Vonage and configure them in your .env file.
-
Sign Up/Log In to Vonage: Go to the Vonage API Dashboard and log in or create a new account.
-
Create a Vonage Application: Applications act as containers for your Vonage settings and credentials.
- Navigate to ""Applications"" in the left-hand menu.
- Click ""Create a new application"".
- Name: Give your application a descriptive name (e.g., ""Node SMS Sender App"").
- Generate Public and Private Key: Click the button to generate key pair. Crucially, save the
private.keyfile immediately. You cannot retrieve it later. Place this file in your project directory (or a secure location referenced by the full path in.env). - Capabilities: Enable the ""Messages"" capability.
- Inbound URL / Status URL: For sending SMS, these are less critical but still required by the dashboard. You can enter placeholders like
https://example.com/webhooks/inboundandhttps://example.com/webhooks/status. If you later implement receiving SMS or delivery receipts, you'll need valid, publicly accessible URLs here (often using ngrok during development).
- Inbound URL / Status URL: For sending SMS, these are less critical but still required by the dashboard. You can enter placeholders like
- Click ""Generate new application"".
- Application ID: On the next screen, copy the generated Application ID.
-
Obtain a Vonage Virtual Number: You need a phone number associated with your Vonage account to send SMS from.
- Navigate to ""Numbers"" > ""Buy numbers"" in the left-hand menu.
- Search for numbers by country and features (ensure ""SMS"" is selected).
- Choose a number and click ""Buy"". Confirm the purchase.
- Navigate to ""Numbers"" > ""Your numbers"". Find the number you just purchased. Copy the number in E.164 format (e.g.,
+12015550123).
-
Link Number to Application (Important): Associate your virtual number with the application you created.
- Go back to ""Applications"" and select the application you created (""Node SMS Sender App"").
- Scroll down to the ""Linked numbers"" section.
- Click ""Link"" next to the virtual number you purchased.
-
Configure Messages API Settings: Ensure your account is set to use the Messages API as the default for SMS.
- Navigate to your main Account Settings.
- Scroll down to ""API settings"".
- Find the ""SMS settings"" section.
- Ensure ""Default SMS Setting"" is set to Messages API. If it's set to ""SMS API"", toggle it and click ""Save changes"".
-
Update
.envFile: Now, open your.envfile and replace the placeholders with the actual values you obtained:dotenv# .env PORT=3000 # Vonage Credentials (Using Application ID & Private Key) VONAGE_APPLICATION_ID=YOUR_COPIED_APPLICATION_ID # Paste the Application ID here VONAGE_PRIVATE_KEY_PATH=./private.key # Ensure this path matches where you saved the key file # Vonage Virtual Number VONAGE_VIRTUAL_NUMBER=+12015550123 # Paste your rented Vonage number in E.164 format
Environment Variable Explanation:
VONAGE_APPLICATION_ID: What: Unique identifier for your Vonage Application. How: Obtained after creating an application in the Vonage Dashboard. Purpose: Used by the SDK (along with the private key) to authenticate requests to the Messages API via JWT.VONAGE_PRIVATE_KEY_PATH: What: File path to the private key file. How: Downloaded when creating the Vonage Application. Purpose: Used by the SDK to sign JWTs for authenticating requests. Keep this file secure and do not commit it to version control.VONAGE_VIRTUAL_NUMBER: What: The Vonage phone number used as the sender ID ('from' number). How: Rented via the Vonage Dashboard (""Numbers"" section). Purpose: Specifies the origin number for the outgoing SMS. Must be in E.164 format.
Security: The private.key file and your .env file contain sensitive credentials. Ensure they are:
- Never committed to Git (use
.gitignore). - Stored securely in your development and production environments.
- Have appropriate file permissions set (restrict read access).
5. Implementing Proper Error Handling, Logging, and Retry Mechanisms
Our current error handling is basic. Let's enhance it.
Error Handling Strategy:
- Validation Errors (Client-side): Catch invalid input (missing params, bad number format) early in the API endpoint and return
400 Bad Request. - SDK/Network Errors (Server-side): Catch errors during the
vonage.messages.send()call. Log detailed information for debugging. Return500 Internal Server Erroror a more specific 5xx code if appropriate. - Vonage API Errors (Server-side): Inspect the error object returned by the SDK (often in
err.response.data) for specific Vonage error codes/messages (e.g., authentication failure, insufficient funds, invalid destination). Log these details and return500(or potentially4xxif the error clearly indicates a client-side issue like an invalid 'to' number rejected by Vonage).
Logging:
console.log and console.error are suitable for development. For production, use a dedicated logging library like Winston or Pino for structured logging, different log levels (debug, info, warn, error), and configurable outputs (file, console, external services).
Example using Winston (Conceptual):
npm install winston- Configure Winston:
javascript
// logger.js const winston = require('winston'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', // Control log level via env var format: winston.format.combine( winston.format.timestamp(), winston.format.json() // Log in JSON format ), transports: [ // Log errors to a file new winston.transports.File({ filename: 'error.log', level: 'error' }), // Log everything to another file new winston.transports.File({ filename: 'combined.log' }), // Log to console (especially in development) new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() // Simple format for console ) }) ], }); module.exports = logger; - Replace
console.log/console.errorwithlogger.info/logger.error:javascript// index.js (modified example using logger) const logger = require('./logger'); // Assuming logger.js exists // ... inside /send-sms endpoint error handler } catch (error) { // Log more context like stack trace and request details logger.error(`API Error processing /send-sms: ${error.message}`, { stack: error.stack, requestBody: req.body }); const statusCode = error.message.includes('Invalid') ? 400 : 500; return res.status(statusCode).json({ /* ... response */ }); } // ... inside sendSms function catch block } catch (err) { // Log error object and any Vonage-specific response data logger.error(""Error sending SMS via Vonage"", { error: err, vonageResponse: err.response?.data }); // ... throw error }
Retry Mechanisms:
Network glitches or temporary Vonage issues can cause failures. Implementing retries can improve reliability. Use libraries like async-retry for straightforward implementation with exponential backoff.
Example using async-retry (Conceptual):
npm install async-retry- Wrap the Vonage call in
retry:javascript// index.js (modified sendSms function with retry logic) const retry = require('async-retry'); const logger = require('./logger'); // Assuming logger is setup async function sendSms(toNumber, messageText) { // ... input validation ... const fromNumber = process.env.VONAGE_VIRTUAL_NUMBER; try { const resp = await retry(async (bail, attempt) => { // bail is a function to stop retrying (e.g., for non-retryable errors) // attempt is the current attempt number logger.info(`Attempt ${attempt} to send SMS to ${toNumber}`); try { const vonageResponse = await vonage.messages.send({ channel: 'sms', message_type: 'text', to: toNumber, from: fromNumber, text: messageText, }); logger.info('SMS submitted successfully:', vonageResponse); return vonageResponse; // Return successful response } catch (sdkError) { // Check for non-retryable errors and call bail() // Examples: Authentication errors, invalid number formats, insufficient funds if (sdkError.response) { const status = sdkError.response.status; const data = sdkError.response.data; // 401 Unauthorized is definitely non-retryable if (status === 401) { logger.error(`Non-retryable Vonage API error (Status: 401 - Unauthorized). Bailing.`); bail(new Error(`Vonage Authentication Error: ${data?.title || 'Unauthorized'}`)); return; // Important } // Some 400 Bad Requests might be non-retryable (e.g., invalid 'to' format) // Check specific error details if possible if (status === 400 && data?.title?.includes('Invalid')) { logger.warn(`Potentially non-retryable Vonage API error (Status: 400 - ${data.title}). Bailing.`); bail(new Error(`Vonage Bad Request Error: ${data?.title} - ${data?.detail || 'Invalid request'}`)); return; // Important } // Add checks for other known non-retryable errors (e.g., specific error codes for insufficient funds) // if (data?.code === 'specific-non-retryable-code') { bail(...); return; } } // If error seems temporary (network issue, 5xx server error), let retry handle it logger.warn(`Attempt ${attempt} failed: ${sdkError.message}. Retrying...`); throw sdkError; // Throw error to trigger retry } }, { retries: 3, // Number of retries factor: 2, // Exponential backoff factor minTimeout: 1000, // Minimum time between retries (ms) maxTimeout: 5000, // Maximum time between retries (ms) onRetry: (error, attempt) => { logger.warn(`Retrying SMS send (Attempt ${attempt}) due to error: ${error.message}`); } }); if (resp && resp.message_uuid) { return resp.message_uuid; } else { logger.error(""Unexpected response structure after retries:"", resp); throw new Error('SMS submission failed after retries or response format unexpected.'); } } catch (err) { // This catch block now catches the final error after all retries failed // or if bail() was called logger.error(""Failed to send SMS after multiple retries:"", { finalError: err }); if (err.response && err.response.data) { // Throw the specific error message from Vonage if available throw new Error(`Vonage API Error: ${err.response.data.title || 'Unknown error'} - ${err.response.data.detail || JSON.stringify(err.response.data)}`); } else { // Throw the error message passed to bail() or the last retry error throw new Error(`Failed to send SMS after retries: ${err.message || 'Unknown error'}`); } } }
Testing Error Scenarios:
- Provide invalid credentials in
.env. - Provide an incorrectly formatted phone number (e.g.,
12345,+1 555 123 4567). - Use a valid number but one not whitelisted on a trial account (see Section 11 - Note: Section 11 was mentioned but not provided in the original input, this reference is kept for context).
- Temporarily disconnect network connectivity.
- Send requests without required parameters (
to,text).
Log Analysis: When issues occur, check your configured log output (e.g., error.log, console, or a log aggregation service) for detailed error messages, stack traces, and potentially the Vonage API response body, which often contains specific error codes and descriptions useful for debugging.
6. Creating a Database Schema and Data Layer
For this specific guide (only sending SMS), a database is not strictly required. However, in a real-world application, you would likely want to track sent messages, their status, cost, and potentially link them to users or events in your system.
If Tracking Were Needed:
-
Schema Design (Example using SQL):
sqlCREATE TABLE sent_sms_log ( id SERIAL PRIMARY KEY, -- Or UUID vonage_message_uuid VARCHAR(36) UNIQUE, -- Store the ID from Vonage client_ref VARCHAR(40), -- Optional client reference sent to Vonage to_number VARCHAR(20) NOT NULL, from_number VARCHAR(20) NOT NULL, message_body TEXT NOT NULL, status VARCHAR(20) DEFAULT 'submitted', -- e.g., submitted, delivered, failed, rejected status_timestamp TIMESTAMPTZ, -- Timestamp of the last status update (requires webhook) submission_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, error_code VARCHAR(50), -- Store error code if failed error_message TEXT, -- Store error description if failed price DECIMAL(10, 5), -- Store cost (requires webhook) currency VARCHAR(3), -- Store currency (requires webhook) -- Optional: Foreign key to your users table -- user_id INT REFERENCES users(id) ); -- Index for common lookups CREATE INDEX idx_sent_sms_log_uuid ON sent_sms_log(vonage_message_uuid); CREATE INDEX idx_sent_sms_log_to_number ON sent_sms_log(to_number); CREATE INDEX idx_sent_sms_log_submission_timestamp ON sent_sms_log(submission_timestamp); -
Entity Relationship Diagram (Conceptual):
mermaiderDiagram SENT_SMS_LOG { INT id PK VARCHAR vonage_message_uuid UK VARCHAR client_ref VARCHAR to_number VARCHAR from_number TEXT message_body VARCHAR status TIMESTAMPTZ status_timestamp TIMESTAMPTZ submission_timestamp VARCHAR error_code TEXT error_message DECIMAL price VARCHAR currency } -
Data Access Layer: You would use an ORM (Object-Relational Mapper) like Prisma or Sequelize in Node.js to interact with the database.
- Prisma Example (Conceptual
schema.prisma):prismamodel SentSmsLog { id Int @id @default(autoincrement()) vonageMessageUuid String? @unique @map(""vonage_message_uuid"") clientRef String? @map(""client_ref"") @db.VarChar(40) toNumber String @map(""to_number"") @db.VarChar(20) fromNumber String @map(""from_number"") @db.VarChar(20) messageBody String @map(""message_body"") @db.Text status String @default(""submitted"") @db.VarChar(20) statusTimestamp DateTime? @map(""status_timestamp"") @db.Timestamptz submissionTimestamp DateTime @default(now()) @map(""submission_timestamp"") @db.Timestamptz errorCode String? @map(""error_code"") @db.VarChar(50) errorMessage String? @map(""error_message"") @db.Text price Decimal? @db.Decimal(10, 5) currency String? @db.VarChar(3) // userId Int? @map(""user_id"") // user User? @relation(fields: [userId], references: [id]) @@map(""sent_sms_log"") @@index([vonageMessageUuid]) @@index([toNumber]) @@index([submissionTimestamp]) } // Example User model if linking // model User { // id Int @id @default(autoincrement()) // email String @unique // smsLogs SentSmsLog[] // }
- Prisma Example (Conceptual
Frequently Asked Questions
How to send SMS messages with Node.js?
Use the Vonage Messages API with the Express.js framework and the @vonage/server-sdk package. This setup allows you to create an API endpoint that accepts recipient numbers and message text for sending SMS messages programmatically within your Node.js application.
What is Vonage Messages API used for?
The Vonage Messages API enables sending messages via various channels like SMS, MMS, WhatsApp, and more. This tutorial uses it specifically for sending SMS text messages from your Node.js application.
Why use dotenv in a Node.js SMS project?
Dotenv securely manages environment variables. It loads credentials like API keys and phone numbers from a .env file, which should never be committed to version control, ensuring sensitive data is protected.
When should I use ngrok with Vonage?
Ngrok is beneficial during development when receiving SMS messages or delivery receipts via webhooks. It creates a publicly accessible tunnel to your local server, allowing Vonage to reach your webhook endpoints.
How to set up Vonage application for SMS messaging?
Create a Vonage Application in the Vonage Dashboard, enable the "Messages" capability, and link your virtual number. Generate public and private keys, saving the private key securely. Set the "Default SMS Setting" to "Messages API" in your Vonage Account Settings.
What is the Vonage virtual number in E.164 format?
An E.164 formatted number is the internationally recognized standard for phone numbers and includes a plus sign (+) followed by the country code and national number. Example: +12015550123. Your Vonage virtual number, used for sending SMS, must be in this format.
How to install necessary packages for sending SMS with Node.js?
Use npm install express @vonage/server-sdk dotenv --save. This command installs Express for creating the API, the Vonage Server SDK for interacting with Vonage, and dotenv for managing environment variables.
How to receive SMS messages with Vonage and Node.js?
Receiving SMS messages involves setting up webhooks on a publicly accessible server, which is covered in separate Vonage documentation. This tutorial focuses solely on sending SMS messages.
What is the purpose of private.key file in Vonage integration?
The private.key file is crucial for authenticating with the Vonage Messages API. It is used by the SDK to digitally sign authentication tokens, and should be kept secure and never committed to version control.
What is the client_ref parameter in Vonage Messages API?
The client_ref is an optional parameter, up to 40 characters, used for your internal tracking. It allows you to correlate messages sent through Vonage with your own records.
How can I test my Vonage SMS API endpoint in Node.js?
You can test the /send-sms endpoint using tools like curl or Postman. Send a POST request with the recipient's phone number ("to") and message text ("text") in the JSON request body.
What are Winston and Pino used for with Node.js?
Winston and Pino are logging libraries for Node.js. They provide structured logging, log levels, and output options, making it easier to manage and analyze log data, especially in production environments.
What is the purpose of async-retry library?
The async-retry library helps manage network hiccups and temporary API issues by implementing retry logic with features like exponential backoff, ensuring more reliable SMS message delivery.
What is the function of message_uuid in the Vonage API response?
The message_uuid is a unique identifier returned by the Vonage API for each successfully submitted message. This UUID is crucial for tracking message status and troubleshooting any delivery issues.