This guide provides a step-by-step walkthrough for building a production-ready Node.js application using the Express framework to send Multimedia Messaging Service (MMS) messages via the Vonage Messages API. We will cover everything from initial project setup to deployment and verification.
You will learn how to configure the Vonage Node.js SDK, handle API credentials securely, build a simple API endpoint to trigger MMS sending, and implement essential features like error handling and logging. By the end, you'll have a functional service capable of sending images programmatically to US-based phone numbers.
Project Overview and Goals
What We're Building: A simple Node.js Express API with a single endpoint (/send-mms
). When this endpoint receives a POST request containing a recipient phone number, a publicly accessible image URL, and an optional caption, it uses the Vonage Messages API to send an MMS message.
Problem Solved: This enables applications to programmatically send rich media content (images) via MMS, useful for notifications, alerts, marketing campaigns, or integrating media sharing into workflows.
Technologies Used:
- Node.js: A JavaScript runtime for building server-side applications. Chosen for its asynchronous nature and large ecosystem (npm).
- Express: A minimal and flexible Node.js web application framework. Chosen for its simplicity in creating API endpoints.
- Vonage Messages API: A unified API for sending messages across various channels, including MMS. Chosen for its robust features and official Node.js SDK.
@vonage/messages
SDK: The official Vonage Node.js SDK specifically for interacting with the Messages API.dotenv
: A module to load environment variables from a.env
file intoprocess.env
. Chosen for easy management of configuration and secrets during development.- Note:
ngrok
is used as a development utility to expose the local server for receiving webhook status updates.
System Architecture:
An API client (like curl, Postman, or another application) sends a POST request to the /send-mms
endpoint of the Node.js/Express API. The Express API uses the @vonage/messages
SDK to call the sendMms
function with the recipient, image URL, and caption. The SDK sends an MMS request to the Vonage Messages API. Vonage delivers the MMS to the recipient's phone. Vonage also sends status updates (webhooks) about the message delivery. During development, these webhooks are sent to an ngrok
tunnel, which forwards them to the /webhooks/status
endpoint on the local Express API server.
Prerequisites:
- Node.js and npm (or yarn) installed locally. Download Node.js
- A Vonage API account. Sign up for free.
- Your Vonage API Key and API Secret (found on the Vonage API Dashboard).
- A Vonage Application ID and associated
private.key
file (we'll create this in Section 4). - A Vonage virtual phone number capable of sending MMS in the US. You can purchase one from the Numbers section of your Vonage dashboard. Note: MMS is primarily supported for A2P (Application-to-Person) use cases to US numbers.
ngrok
installed and authenticated (a free account is sufficient). Download ngrok. Used for receiving webhook status updates during development.- A target phone number (must be a US number) to receive the MMS. If using a trial Vonage account, this number must be added to the whitelisted numbers in your dashboard.
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 the project, then navigate into it.
mkdir vonage-mms-sender cd vonage-mms-sender
-
Initialize Node.js Project: This command creates a
package.json
file.npm init -y
-
Install Dependencies: We need Express for the server, the Vonage Messages SDK, and
dotenv
for environment variables.npm install express @vonage/messages dotenv
-
Set Up Project Structure: Create directories for source code and configuration.
mkdir src config
-
Create Environment File (
.env
): Create a file named.env
in the project root. This file will store sensitive credentials and configuration. Never commit this file to version control.# .env # Vonage API Credentials (From Dashboard -> API Settings) VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Vonage Application Credentials (Generated in Dashboard -> Applications) VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Relative path to your private key file # Vonage Number (From Dashboard -> Numbers -> Your Numbers) VONAGE_FROM_NUMBER=YOUR_VONAGE_US_MMS_NUMBER # Must be MMS capable US number, e.g., 14155550100 # Server Configuration PORT=3000 # Ngrok URL (For development status webhooks) # Example: https://YOUR_UNIQUE_SUBDOMAIN.ngrok-free.app # Leave blank initially, fill in after running ngrok NGROK_BASE_URL=
VONAGE_API_KEY
,VONAGE_API_SECRET
: Found directly on your Vonage dashboard. Required for basic SDK authentication.VONAGE_APPLICATION_ID
,VONAGE_PRIVATE_KEY_PATH
: Obtained when creating a Vonage Application (covered in Section 4). Used for authenticating requests specifically for the Messages API.VONAGE_FROM_NUMBER
: Your purchased Vonage US number capable of sending MMS. Enter it without+
or spaces.PORT
: The port your Express server will listen on.NGROK_BASE_URL
: The public URL provided by ngrok, used for receiving status webhooks during development.
-
Create Git Ignore File (
.gitignore
): Create a file named.gitignore
in the project root to prevent committing sensitive files and generated folders.# .gitignore node_modules .env private.key npm-debug.log
-
Configuration Loading: Create a file
config/index.js
to load and export environment variables.// config/index.js require('dotenv').config(); // Load variables from .env into process.env const config = { vonage: { apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, applicationId: process.env.VONAGE_APPLICATION_ID, privateKeyPath: process.env.VONAGE_PRIVATE_KEY_PATH, fromNumber: process.env.VONAGE_FROM_NUMBER, }, server: { port: process.env.PORT || 3000, }, ngrokBaseUrl: process.env.NGROK_BASE_URL, // Used later for webhook setup if needed }; // Basic validation if (!config.vonage.apiKey || !config.vonage.apiSecret || !config.vonage.applicationId || !config.vonage.privateKeyPath || !config.vonage.fromNumber) { console.error('Missing required Vonage credentials in .env file'); process.exit(1); // Exit if critical config is missing } module.exports = config;
2. Implementing Core Functionality: Vonage Service
Let's create a dedicated service file to handle interactions with the Vonage Messages API. This promotes separation of concerns.
-
Create Service File: Create
src/services/vonageService.js
. -
Implement MMS Sending Logic: Add the following code to initialize the Vonage SDK and define the function to send MMS.
// src/services/vonageService.js const { Messages, MMSImage } = require('@vonage/messages'); const config = require('../../config'); // Load our configuration // Initialize the Vonage Messages client // The Messages API uses Application ID + Private Key for JWT authentication, // alongside the standard API Key + Secret. const vonageMessages = new Messages({ apiKey: config.vonage.apiKey, apiSecret: config.vonage.apiSecret, applicationId: config.vonage.applicationId, privateKey: config.vonage.privateKeyPath, // SDK reads the file path }); /** * Sends an MMS message using the Vonage Messages API. * @param {string} to - The recipient phone number (E.164 format, e.g., 14155550101). * @param {string} imageUrl - The publicly accessible URL of the image to send. * @param {string} [caption=''] - Optional caption for the image. * @returns {Promise<object>} - A promise that resolves with the Vonage API response. * @throws {Error} - Throws an error if the message sending fails. */ const sendMms = async (to, imageUrl, caption = '') => { console.log(`Attempting to send MMS to ${to} from ${config.vonage.fromNumber}`); // Validate image URL (basic check) if (!imageUrl || !imageUrl.startsWith('http')) { throw new Error('Invalid or missing image URL. Must be a public HTTP/HTTPS URL.'); } // Construct the MMS message payload using the MMSImage class const messagePayload = new MMSImage({ to: to, from: config.vonage.fromNumber, // Your Vonage MMS-capable number image: { url: imageUrl, caption: caption, // Optional caption }, channel: 'mms', // Specify the channel as MMS message_type: 'image', // Specify message type }); try { // Send the message using the SDK const response = await vonageMessages.send(messagePayload); console.log(`MMS submitted successfully to Vonage. Message UUID: ${response.message_uuid}`); return response; // Contains message_uuid } catch (error) { console.error(`Error sending MMS via Vonage: ${error.message}`); // Log more details for debugging if available if (error.response && error.response.data) { console.error('Vonage API Error Details:', JSON.stringify(error.response.data, null, 2)); } // Re-throw a structured error or handle appropriately throw new Error(`Failed to send MMS: ${error.message || 'Unknown Vonage API error'}`); } }; module.exports = { sendMms, };
Why this approach?
@vonage/messages
SDK: This is the specific SDK package designed for the Vonage Messages API, which handles MMS. Older SDKs or different methods might target the legacy SMS API.MMSImage
Class: The SDK provides convenient classes likeMMSImage
to structure the payload correctly for different message types, reducing errors.- Application ID + Private Key: The Messages API often requires JWT authentication generated from your Application ID and private key for sending messages via specific channels like MMS, WhatsApp, etc. The SDK handles JWT generation internally when provided with the App ID and private key path.
- Async/Await: Using
async/await
makes asynchronous code cleaner and easier to read compared to raw promises or callbacks. - Error Handling: The
try...catch
block ensures that errors from the Vonage API are caught, logged, and potentially transformed before being propagated, allowing the calling code (our API endpoint) to handle them gracefully.
3. Building the API Layer with Express
Now, let's create the Express server and the /send-mms
endpoint.
-
Create Server File: Create
src/server.js
. -
Implement Express Server and Endpoint:
// src/server.js const express = require('express'); const config = require('../config'); // Load configuration const { sendMms } = require('./services/vonageService'); // Import our service function const app = express(); // --- Middleware --- // Parse JSON request bodies app.use(express.json()); // Parse URL-encoded request bodies app.use(express.urlencoded({ extended: true })); // Simple request logger middleware (optional but helpful) app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }); // --- Routes --- // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() }); }); // Endpoint to send MMS app.post('/send-mms', async (req, res, next) => { // Basic Input Validation (Consider using a library like express-validator for production) const { to, imageUrl, caption } = req.body; if (!to || !imageUrl) { return res.status(400).json({ success: false, message: 'Missing required fields: `to` and `imageUrl` are required.', }); } // Simple validation for phone number format (basic example) if (!/^\d{10,15}$/.test(to.replace(/^\+/, ''))) { return res.status(400).json({ success: false, message: 'Invalid `to` phone number format. Use E.164 or digits only (10-15 digits).', }); } try { // Call the Vonage service function const result = await sendMms(to, imageUrl, caption); // Send success response res.status(202).json({ // 202 Accepted: Request received, processing initiated success: true, message: 'MMS submission accepted by Vonage.', data: result, // Contains message_uuid }); } catch (error) { // Pass error to the error handling middleware next(error); } }); // --- Webhook Endpoint (for status updates) --- // Vonage sends status updates here (POST or GET depending on config) app.all('/webhooks/status', (req, res) => { console.log('Webhook received (Status):'); console.log('Headers:', JSON.stringify(req.headers, null, 2)); console.log('Body:', JSON.stringify(req.body, null, 2)); // Body format depends on Vonage settings console.log('Query Params:', JSON.stringify(req.query, null, 2)); // IMPORTANT: Always respond with 200 OK to Vonage webhooks // Otherwise, Vonage will retry, potentially causing duplicate processing. res.status(200).send('OK'); }); // --- Global Error Handler --- // This middleware catches errors passed via next(error) app.use((err, req, res, next) => { console.error('Global Error Handler Caught:', err.stack || err); // Determine status code - default to 500 const statusCode = err.statusCode || 500; res.status(statusCode).json({ success: false, message: err.message || 'An internal server error occurred.', // Optionally include stack trace in development ...(process.env.NODE_ENV === 'development' && { stack: err.stack }), }); }); // --- Start Server --- const PORT = config.server.port; app.listen(PORT, () => { console.log(`Server listening on http://localhost:${PORT}`); console.log(`Health check available at http://localhost:${PORT}/health`); if (config.ngrokBaseUrl) { console.log(`Status Webhook expected at: ${config.ngrokBaseUrl}/webhooks/status`); } else { console.warn(""NGROK_BASE_URL not set in .env - Status webhooks won't be received locally without ngrok.""); } });
-
Add Start Script to
package.json
: Modify thescripts
section in yourpackage.json
:// package.json { // ... other properties ""scripts"": { ""start"": ""node src/server.js"", ""dev"": ""nodemon src/server.js"" // Optional: if you install nodemon (`npm install --save-dev nodemon`) }, // ... other properties }
4. Integrating with Vonage: Dashboard Setup
Before running the code, you need to configure your Vonage account and application correctly.
-
Get API Key and Secret:
- Navigate to your Vonage API Dashboard.
- Your
API key
andAPI secret
are displayed at the top. - Copy these values into the
VONAGE_API_KEY
andVONAGE_API_SECRET
fields in your.env
file.
-
Set Default SMS API to
Messages API
:- Go to Account Settings.
- Scroll down to ""API settings"".
- Under ""Default SMS Setting"", ensure
Messages API
is selected. This is crucial for MMS and the@vonage/messages
SDK to work correctly. - Click ""Save changes"".
-
Create a Vonage Application:
- Navigate to Applications in the dashboard.
- Click ""Create a new application"".
- Enter an Application name (e.g., ""Node MMS Sender"").
- Click ""Generate public and private key"". This will automatically download a
private.key
file to your computer and populate the public key field. - IMPORTANT: Move the downloaded
private.key
file into the root directory of your Node.js project (the same level as your.env
file). Ensure theVONAGE_PRIVATE_KEY_PATH
in your.env
file points to it (e.g.,./private.key
). - Enable the ""Messages"" capability.
- You'll see fields for Inbound URL and Status URL. These are webhook endpoints where Vonage sends data to your application.
- Status URL: This is where Vonage sends updates about the delivery status of your outgoing MMS messages. For development, we'll use ngrok. Leave it blank for now, or put a placeholder like
http://localhost:3000/webhooks/status
. We will update this after starting ngrok. Set the HTTP method toPOST
. - Inbound URL: This is where Vonage sends incoming messages sent to your Vonage number. We are not handling inbound MMS in this guide, but it's required. You can use the same placeholder:
http://localhost:3000/webhooks/inbound
. Set the HTTP method toPOST
.
- Status URL: This is where Vonage sends updates about the delivery status of your outgoing MMS messages. For development, we'll use ngrok. Leave it blank for now, or put a placeholder like
- Click ""Create application"".
- You will be redirected to the application's page. Copy the Application ID displayed.
- Paste this ID into the
VONAGE_APPLICATION_ID
field in your.env
file.
-
Link Your Vonage Number:
- On the application page you were just directed to (or by navigating back to Applications and clicking your app name), scroll down to the ""Linked numbers"" section.
- Find your US MMS-capable Vonage number in the list.
- Click the ""Link"" button next to it. If you don't have a suitable number, click ""Buy numbers"".
- Ensure the
VONAGE_FROM_NUMBER
in your.env
file matches this linked number (use E.164 format without+
, e.g.,14155550100
).
-
Set up ngrok (for Status Webhooks during Development):
-
Open a new terminal window (keep your Node.js app terminal separate).
-
Run ngrok to expose the port your Express app listens on (defined in
.env
, default 3000).ngrok http 3000
-
ngrok will display output including a
Forwarding
URL (e.g.,https://random-subdomain.ngrok-free.app
). Copy this HTTPS URL. -
Paste this base URL into the
NGROK_BASE_URL
field in your.env
file. -
Go back to your application settings in the Vonage Dashboard.
-
Edit the Status URL under the ""Messages"" capability. Set it to your ngrok Forwarding URL followed by the webhook path defined in
server.js
:YOUR_NGROK_URL/webhooks/status
(e.g.,https://random-subdomain.ngrok-free.app/webhooks/status
). Ensure the method isPOST
. -
Update the Inbound URL similarly:
YOUR_NGROK_URL/webhooks/inbound
. -
Click ""Save changes"".
-
5. Error Handling, Logging, and Retry Mechanisms
Our current setup includes basic error handling:
-
Service Level (
vonageService.js
): Catches errors during thevonageMessages.send
call, logs details, and throws a new error. -
API Level (
server.js
):- Uses
try...catch
in the route handler to catch errors from the service. - Passes caught errors to a global Express error handling middleware using
next(error)
. - The global error handler logs the error stack and sends a standardized JSON error response to the client.
- Basic input validation prevents obviously bad requests.
- Uses
-
Logging: Uses
console.log
andconsole.error
. For production, consider structured logging libraries like Winston or Pino for better log management and analysis.# Example: Add Winston (Optional) npm install winston
You would then configure Winston (e.g., log to files, different levels) and replace
console.log/error
calls. -
Retry Mechanisms:
- Vonage Retries: Vonage automatically retries sending webhooks (like the status update) to your specified URL if it doesn't receive a
200 OK
response quickly. This is whyres.status(200).send('OK');
in the/webhooks/status
endpoint is crucial. - Application Retries: For failures in the initial
sendMms
call (e.g., temporary network issue to Vonage, intermittent Vonage API errors), you might implement application-level retries. Libraries likeasync-retry
can help implement strategies like exponential backoff. However, be cautious about retrying errors that are clearly permanent (e.g., invalid credentials, invalid recipient number).
// Conceptual example using async-retry (Install: npm install async-retry) // In vonageService.js // const retry = require('async-retry'); // // const sendMmsWithRetry = async (to, imageUrl, caption = '') => { // await retry(async bail => { // // If Vonage returns specific errors indicating non-transient failure, use bail() // // Example: if (error.response && error.response.data.code === 'INVALID_CREDENTIALS') { // // bail(new Error('Non-retryable error: Invalid Credentials')); // // return; // // } // await sendMms(to, imageUrl, caption); // Call original function // }, { // retries: 3, // Number of retries // factor: 2, // Exponential backoff factor // minTimeout: 1000, // Initial timeout // onRetry: (error, attempt) => console.warn(`Retry attempt ${attempt} failed: ${error.message}`), // }); // };
- Vonage Retries: Vonage automatically retries sending webhooks (like the status update) to your specified URL if it doesn't receive a
6. Creating a Database Schema and Data Layer (Conceptual)
While this guide focuses solely on sending MMS, a production application often needs to track the status of sent messages. This typically involves a database.
-
Conceptual Schema: You might have a
messages
table:CREATE TABLE messages ( id SERIAL PRIMARY KEY, -- Or UUID vonage_message_uuid VARCHAR(255) UNIQUE, -- UUID returned by Vonage recipient_number VARCHAR(20) NOT NULL, sender_number VARCHAR(20) NOT NULL, image_url TEXT, caption TEXT, status VARCHAR(50) DEFAULT 'submitted', -- e.g., submitted, delivered, failed, unknown status_timestamp TIMESTAMPTZ, vonage_status_code VARCHAR(10), -- Code received in status webhook error_description TEXT, -- Error details from webhook submitted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP );
-
Data Layer:
- You would use an ORM (like Prisma, Sequelize, TypeORM) or a database client (like
pg
for PostgreSQL) to interact with this table. - On Send: After successfully calling
sendMms
and getting themessage_uuid
, you'd insert a new record into themessages
table withstatus = 'submitted'
. - On Status Webhook: When the
/webhooks/status
endpoint receives an update from Vonage containing amessage_uuid
, you'd find the corresponding record in themessages
table and update itsstatus
,status_timestamp
,vonage_status_code
, and potentiallyerror_description
based on the webhook data.
- You would use an ORM (like Prisma, Sequelize, TypeORM) or a database client (like
Implementing this is beyond the scope of this specific guide but is a critical next step for robust tracking.
7. Adding Security Features
Security is paramount. Here are essential considerations:
-
Input Validation and Sanitization:
- We added basic checks in
server.js
. For production, use a dedicated library likeexpress-validator
for more robust validation (e.g., checking phone number formats more strictly, validating URL structure, limiting caption length). - Sanitize inputs to prevent Cross-Site Scripting (XSS) if captions or other data are ever displayed in a web context.
# Example: Add express-validator (Optional) npm install express-validator
// Example usage in server.js route // const { body, validationResult } = require('express-validator'); // // app.post('/send-mms', // body('to').isMobilePhone('any', { strictMode: false }).withMessage('Invalid phone number format'), // body('imageUrl').isURL({ protocols: ['http', 'https'] }).withMessage('Invalid or non-HTTPS image URL'), // body('caption').optional().isLength({ max: 1000 }).escape(), // Escape for safety // async (req, res, next) => { // const errors = validationResult(req); // if (!errors.isEmpty()) { // return res.status(400).json({ success: false, errors: errors.array() }); // } // // ... rest of the route logic ... // });
- We added basic checks in
-
Protect API Credentials:
- NEVER commit
.env
orprivate.key
to Git (enforced by.gitignore
). - In production environments, use secure methods for managing secrets (e.g., environment variables injected by the platform, AWS Secrets Manager, HashiCorp Vault). Do not deploy
.env
files to production servers.
- NEVER commit
-
Rate Limiting:
- Protect your API endpoint from abuse by implementing rate limiting.
# Example: Add express-rate-limit (Optional) npm install express-rate-limit
// Example usage in server.js (apply before routes) // const rateLimit = require('express-rate-limit'); // // const limiter = rateLimit({ // windowMs: 15 * 60 * 1000, // 15 minutes // max: 100, // Limit each IP to 100 requests per windowMs // message: 'Too many requests from this IP, please try again after 15 minutes', // }); // app.use('/send-mms', limiter); // Apply specifically to the send endpoint
-
API Endpoint Authentication/Authorization:
- The
/send-mms
endpoint itself is currently open. In a real application, you would protect it. Methods include:- API Keys: Require clients to send a secret API key in a header (e.g.,
X-API-Key
). Validate this key in middleware. - JWT Tokens: If users log in to your application, issue JWTs and require them for API calls.
- IP Whitelisting: Restrict access to specific IP addresses if applicable.
- API Keys: Require clients to send a secret API key in a header (e.g.,
- The
-
Webhook Security:
- Vonage supports signing webhooks using either a shared secret or your application's private key (JWT). Validating these signatures ensures that incoming webhook requests genuinely originated from Vonage. Refer to the Vonage documentation on securing webhooks. Implementing signature validation is highly recommended for production.
8. Handling Special Cases Relevant to MMS
- US Number Requirement: Vonage MMS sending is primarily supported from US/Canada numbers to US numbers. Sending to international numbers or from non-US/Canada numbers may not work or have limitations. Verify your ""From"" number capability and target number location.
- Publicly Accessible Image URL: The
imageUrl
provided must resolve to a publicly accessible image file over HTTP or HTTPS. Vonage needs to fetch this image to send it. Local file paths or private URLs will not work. - Supported Image Formats: Vonage typically supports common formats like
.jpg
,.jpeg
,.png
, and.gif
. Check the latest Vonage documentation for specifics and size limits. - Demo Account Limitations: If using a free trial Vonage account, you can only send messages (SMS/MMS) to phone numbers that you have verified and added to your test numbers list in the dashboard. Attempting to send to other numbers will result in a ""Non-Whitelisted Destination"" error.
- Character Encoding/Captions: Ensure captions use standard character sets (like UTF-8). Special characters might be handled differently by recipient carriers. Keep captions concise.
- A2P Compliance: Ensure your use case complies with Application-to-Person messaging regulations and Vonage's acceptable use policies, especially regarding consent if sending marketing messages.
9. Implementing Performance Optimizations
For this specific use case (sending individual MMS messages triggered by an API call), major performance bottlenecks are less likely within the Node.js application itself compared to external factors (Vonage API latency, carrier delivery). However:
- Asynchronous Operations: Node.js and the Vonage SDK are inherently asynchronous. Ensure you are not blocking the event loop with synchronous code in your route handlers or services. Using
async/await
correctly helps manage this. - Resource Usage: Monitor CPU and memory usage under load. If the service handles high traffic, ensure efficient JSON parsing and minimize unnecessary computation per request.
- Connection Pooling: If interacting with a database (Section 6), ensure your ORM or client uses connection pooling effectively.
- Caching: Caching is generally not applicable for the sending action itself. However, if you frequently retrieve data before sending (e.g., user details, image URLs from a CMS), caching that retrieved data can improve the performance of the steps leading up to the
sendMms
call. - Load Testing: Use tools like
k6
,artillery
, or ApacheBench (ab
) to simulate traffic against your/send-mms
endpoint and identify potential bottlenecks under load. Monitor Vonage API response times and error rates during tests. - Profiling: Use Node.js built-in profiler or tools like Clinic.js to analyze CPU usage and identify performance hotspots within your code if needed.
10. Adding Monitoring, Observability, and Analytics
To understand how your service behaves in production:
-
Health Checks: The
/health
endpoint provides a basic check. Monitoring systems (like Prometheus, Datadog, UptimeRobot) can periodically ping this endpoint to ensure the service is running. -
Performance Metrics:
- Track the latency of requests to the
/send-mms
endpoint. - Track the latency of calls to the Vonage API (
sendMms
function duration). - Monitor the rate of successful requests vs. error responses (HTTP 4xx/5xx).
- Tools: Prometheus with
prom-client
, Datadog APM, New Relic.
- Track the latency of requests to the
-
Error Tracking:
- Integrate an error tracking service (e.g., Sentry, Bugsnag, Datadog Error Tracking). These services capture unhandled exceptions and errors passed to the global error handler, group them, and provide detailed context (stack traces, request data).
# Example: Add Sentry (Optional) npm install @sentry/node @sentry/tracing
// Example integration in server.js (early in the file) // const Sentry = require('@sentry/node'); // const Tracing = require('@sentry/tracing'); // If using tracing // // Sentry.init({ // dsn: ""YOUR_SENTRY_DSN"", // integrations: [ // // enable HTTP calls tracing // new Sentry.Integrations.Http({ tracing: true }), // // enable Express.js middleware tracing // new Tracing.Integrations.Express({ app }), // ], // // Set tracesSampleRate to 1.0 to capture 100% // // of transactions for performance monitoring. // // Adjust in production! // tracesSampleRate: 1.0, // }); // // // RequestHandler creates a separate execution context using domains, so that every // // transaction/span/breadcrumb is attached to its own Hub instance // app.use(Sentry.Handlers.requestHandler()); // // TracingHandler creates a trace for every incoming request // app.use(Sentry.Handlers.tracingHandler()); // // // ... Your routes BEFORE the error handler ... // // // The error handler must be before any other error middleware and after all controllers // app.use(Sentry.Handlers.errorHandler()); // // // Optional fallthrough error handler // app.use(function onError(err, req, res, next) { // // The error id is attached to `res.sentry` to be returned // // and optionally displayed to the user for support. // res.statusCode = 500; // res.end(res.sentry + ""\n""); // });
-
Structured Logging:
- Replace
console.log/error
with a structured logging library (Winston, Pino). Log events with consistent formats (JSON is common), including timestamps, severity levels, request IDs, and relevant context (e.g.,message_uuid
,recipient
). - Send logs to a centralized logging platform (e.g., Datadog Logs, ELK Stack, Splunk, Grafana Loki) for searching, analysis, and alerting.
- Replace
-
Vonage Dashboard & Analytics:
- Regularly check the Vonage Messages API Dashboard for insights into delivery rates, error codes, and usage trends provided directly by Vonage. This complements your application-level monitoring.
By implementing these observability practices, you gain crucial visibility into your MMS sending service's health, performance, and potential issues.