This guide provides a step-by-step walkthrough for building a Node.js application using the Express framework to send MMS messages via the Vonage Messages API. We'll cover everything from project setup and core implementation to security, error handling, and deployment considerations.
By the end of this tutorial, you will have a functional Express API endpoint capable of accepting requests to send MMS messages, complete with necessary configurations and best practices for a production-ready service.
Project Overview and Goals
What We're Building:
We will create a simple REST API using Node.js and Express. This API will expose a single endpoint (/send-mms
) that accepts a recipient phone number, an image URL, and an optional caption. Upon receiving a valid request, the API will use the Vonage Messages API to send an MMS containing the specified image and caption to the recipient.
Problem Solved:
This guide addresses the need for developers to programmatically send MMS messages, enabling applications to share rich media content (images) with users via their mobile devices, directly from a backend service.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications.
- Express: A minimal and flexible Node.js web application framework used to create the API layer.
- Vonage Messages API: A multi-channel API enabling communication via SMS, MMS, WhatsApp, etc. We'll use it specifically for sending MMS.
@vonage/messages
SDK: The official Vonage Node.js client library specifically designed for interacting with the Messages API.dotenv
: A module to load environment variables from a.env
file, keeping sensitive credentials secure and separate from code.
Prerequisites:
- Node.js and npm (or yarn): Installed on your development machine.
- Vonage API Account: Sign up if you don't have one. You'll get free credits to start.
- Vonage API Key and Secret: Found on your Vonage API Dashboard.
- Vonage Application: A Messages API-enabled application created in the Vonage Dashboard. You'll need the Application ID and the Private Key file.
- Vonage Virtual Number: An MMS-capable US number rented from Vonage and linked to your Vonage Application.
- Publicly Accessible Image URL: The URL of the image you want to send (Vonage supports
.jpg
,.jpeg
, and.png
). - (Optional)
ngrok
or similar: If you need to test inbound webhooks for status updates (not strictly required for sending only). - (Optional but Recommended) Postman or
curl
: For testing the API endpoint.
System Architecture:
A simplified view of the system:
[Client Application / Test Tool (e.g., Postman)]
|
| HTTP POST Request (/send-mms with recipient, image URL, caption)
v
[Node.js / Express API Server] --- (Loads Credentials from .env)
|
| Uses @vonage/messages SDK
v
[Vonage Messages API]
|
| Sends MMS
v
[Recipient's Mobile Device]
Expected Outcome:
A running Node.js Express server with an endpoint that successfully sends an MMS message via Vonage when triggered.
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 npm: Initialize the project using npm. The
-y
flag accepts default settings.npm init -y
-
Enable ES Modules: Since the code examples use ES Module
import
syntax, you need to tell Node.js to treat.js
files as modules. Open the generatedpackage.json
file and add the following line at the top level:"type": "module",
Your
package.json
should look something like this initially:{ "name": "vonage-mms-sender", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
-
Install Dependencies: Install Express, the Vonage Messages SDK, and
dotenv
.npm install express @vonage/messages dotenv
-
Create Core Files: Create the main application file and a file for environment variables.
touch index.js .env .gitignore
-
Configure
.gitignore
: Addnode_modules
and.env
to your.gitignore
file to prevent committing dependencies and sensitive credentials.# .gitignore node_modules/ .env *.key # Also ignore private key files if stored locally
-
Set up Environment Variables (
.env
): Open the.env
file and add the following placeholders. You will replace these with your actual Vonage credentials later.# .env # Vonage API Credentials VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Vonage Application Credentials VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Path to your downloaded private key file # Vonage Number & API Port VONAGE_MMS_FROM_NUMBER=YOUR_VONAGE_MMS_NUMBER # Your purchased Vonage number (E.164 format) PORT=3000 # Port for the Express server
VONAGE_PRIVATE_KEY_PATH
: Assumes you will save the downloadedprivate.key
file in the root of your project directory. Adjust the path if you store it elsewhere. Crucially, ensure this file is kept secure and is not committed to version control.
-
Project Structure: Your basic project structure should now look like this:
vonage-mms-sender/ ├── .env ├── .gitignore ├── index.js ├── node_modules/ ├── package-lock.json └── package.json # (You will add private.key here later)
2. Implementing Core Functionality
Now, let's write the core logic to initialize the Vonage client and define the function responsible for sending the MMS.
Open index.js
and add the following code:
// index.js
import express from 'express';
import 'dotenv/config'; // Load environment variables from .env file
import { Messages } from '@vonage/messages';
import { MMSImage } from '@vonage/messages'; // Specifically import MMSImage
import fs from 'fs'; // Needed to read the private key file
// --- Vonage Client Initialization ---
// Function to read the private key content
const readPrivateKey = (path) => {
try {
return fs.readFileSync(path, 'utf8');
} catch (err) {
console.error(`Error reading private key file at ${path}:`, err);
throw new Error('Could not read private key file.');
}
};
// Ensure necessary environment variables are loaded
const requiredEnvVars = [
'VONAGE_API_KEY',
'VONAGE_API_SECRET',
'VONAGE_APPLICATION_ID',
'VONAGE_PRIVATE_KEY_PATH',
'VONAGE_MMS_FROM_NUMBER',
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Error: Missing required environment variable ${envVar}. Check your .env file.`);
process.exit(1); // Exit if critical config is missing
}
}
const vonageClient = new Messages({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: readPrivateKey(process.env.VONAGE_PRIVATE_KEY_PATH), // Pass key content, not path
});
// --- MMS Sending Function ---
/**
* Sends an MMS message using the Vonage Messages API.
* @param {string} recipientNumber - The recipient's phone number in E.164 format.
* @param {string} imageUrl - The publicly accessible URL of the image to send.
* @param {string} [caption] - Optional caption text for the image.
* @returns {Promise<object>} - The response object from the Vonage API.
*/
async function sendMmsMessage(recipientNumber, imageUrl, caption) {
console.log(`Attempting to send MMS to ${recipientNumber} from ${process.env.VONAGE_MMS_FROM_NUMBER}`);
const messageRequest = new MMSImage({
to: recipientNumber,
from: process.env.VONAGE_MMS_FROM_NUMBER,
image: {
url: imageUrl,
caption: caption || undefined, // Add caption only if provided
},
// Optional: client_ref can be used to correlate messages
// client_ref: `my-mms-message-${Date.now()}`
});
try {
const response = await vonageClient.send(messageRequest);
console.log('MMS Sent Successfully:', response);
return response; // Contains message_uuid
} catch (error) {
console.error('Error sending MMS via Vonage:', error?.response?.data || error.message);
// Rethrow or handle specific errors as needed
throw error;
}
}
// (API Layer will be added in the next step)
// Placeholder for starting the server (will be moved later)
// const PORT = process.env.PORT || 3000;
// const app = express(); // Will be defined properly in the API layer section
// app.listen(PORT, () => console.log(`Server temporarily listening on port ${PORT}`));
// Example Usage (for testing core logic directly, remove later)
/*
async function testSend() {
try {
const testRecipient = 'REPLACE_WITH_YOUR_TEST_PHONE_NUMBER'; // E.g., '14155551212'
const testImageUrl = 'https://placekitten.com/200/300'; // Must be publicly accessible
const testCaption = 'Hello from Vonage MMS!';
if (testRecipient === 'REPLACE_WITH_YOUR_TEST_PHONE_NUMBER') {
console.warn(""Please replace 'REPLACE_WITH_YOUR_TEST_PHONE_NUMBER' before running the test."");
return;
}
await sendMmsMessage(testRecipient, testImageUrl, testCaption);
} catch (err) {
console.error(""Test send failed."");
}
}
// testSend(); // Uncomment to test sending directly (ensure .env is configured)
*/
// Export the sending function if you plan to structure into multiple files
// export { sendMmsMessage };
Explanation:
- Imports: We import
express
(for the future API),dotenv/config
(to load.env
immediately),Messages
andMMSImage
from@vonage/messages
, andfs
to read the private key file. - Private Key Reading: A helper function
readPrivateKey
is added to read the key file content. The Vonage SDK expects the content of the key, not the file path. Error handling is included. - Environment Variable Check: Added a check to ensure all required environment variables are present before attempting to initialize the client. This prevents runtime errors due to missing configuration.
- Vonage Client Initialization: We create an instance of the
Messages
client, passing the API Key, Secret, Application ID, and the content of the private key, all read from environment variables. sendMmsMessage
Function:- Takes the recipient number (E.164 format, e.g.,
14155551212
), image URL, and optional caption. - Creates an
MMSImage
message object, setting theto
,from
, andimage
(withurl
and optionalcaption
). - Uses
vonageClient.send()
to dispatch the message. This is an asynchronous operation, so we useasync/await
. - Includes
try...catch
for basic error handling, logging success or failure details. The error object from Vonage often contains useful details inerror?.response?.data
.
- Takes the recipient number (E.164 format, e.g.,
- Placeholder/Test Code: The commented-out
testSend
function and server start lines allow you to test the core sending logic directly (node index.js
) before building the full API. Remember to replace the placeholder recipient number and ensure your.env
file is correctly configured before uncommenting and runningtestSend
.
3. Building a Complete API Layer
Now, let's wrap our core MMS sending function in an Express API endpoint.
Modify index.js
to include the Express server setup:
// index.js
import express from 'express';
import 'dotenv/config';
import { Messages } from '@vonage/messages';
import { MMSImage } from '@vonage/messages';
import fs from 'fs';
// --- Vonage Client Initialization ---
const readPrivateKey = (path) => {
// ... (keep the function from Step 2)
try {
return fs.readFileSync(path, 'utf8');
} catch (err) {
console.error(`Error reading private key file at ${path}:`, err);
throw new Error('Could not read private key file.');
}
};
const requiredEnvVars = [ /* ... (keep the array from Step 2) */
'VONAGE_API_KEY',
'VONAGE_API_SECRET',
'VONAGE_APPLICATION_ID',
'VONAGE_PRIVATE_KEY_PATH',
'VONAGE_MMS_FROM_NUMBER',
];
for (const envVar of requiredEnvVars) { /* ... (keep the check from Step 2) */
if (!process.env[envVar]) {
console.error(`Error: Missing required environment variable ${envVar}. Check your .env file.`);
process.exit(1);
}
}
const vonageClient = new Messages({ /* ... (keep initialization from Step 2) */
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: readPrivateKey(process.env.VONAGE_PRIVATE_KEY_PATH),
});
// --- MMS Sending Function ---
async function sendMmsMessage(recipientNumber, imageUrl, caption) {
// ... (keep the function implementation from Step 2)
console.log(`Attempting to send MMS to ${recipientNumber} from ${process.env.VONAGE_MMS_FROM_NUMBER}`);
const messageRequest = new MMSImage({ /* ... */
to: recipientNumber,
from: process.env.VONAGE_MMS_FROM_NUMBER,
image: {
url: imageUrl,
caption: caption || undefined,
},
});
try {
const response = await vonageClient.send(messageRequest);
console.log('MMS Sent Successfully:', response);
return response;
} catch (error) {
console.error('Error sending MMS via Vonage:', error?.response?.data || error.message);
throw error;
}
}
// --- Express API Setup ---
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json()); // Parse JSON request bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded request bodies
// API Endpoint: POST /send-mms
app.post('/send-mms', async (req, res) => {
const { recipientNumber, imageUrl, caption } = req.body;
// Basic Input Validation
if (!recipientNumber || !imageUrl) {
console.warn('Received invalid request: Missing recipientNumber or imageUrl');
return res.status(400).json({
success: false,
message: 'Missing required fields: recipientNumber and imageUrl are required.',
});
}
// Simple validation for recipient number format (basic check)
// A more robust validation (e.g., using libphonenumber-js) is recommended for production
if (!/^\+?[1-9]\d{1,14}$/.test(recipientNumber)) {
console.warn(`Received invalid recipient number format: ${recipientNumber}`);
return res.status(400).json({
success: false,
message: 'Invalid recipient phone number format. Use E.164 format (e.g., +14155551212).',
});
}
// Simple validation for image URL format (basic check)
try {
new URL(imageUrl);
} catch (_) {
console.warn(`Received invalid image URL format: ${imageUrl}`);
return res.status(400).json({
success: false,
message: 'Invalid image URL format.',
});
}
try {
const result = await sendMmsMessage(recipientNumber, imageUrl, caption);
res.status(200).json({
success: true,
message: 'MMS send request accepted.',
messageId: result.message_uuid, // Include the Vonage message ID
});
} catch (error) {
// Log the detailed error server-side
console.error('API Error - Failed to send MMS:', error);
// Provide a generic error message to the client
res.status(500).json({
success: false,
message: 'Failed to send MMS. Please check server logs for details.',
// Optionally include specific error codes if safe to expose
// errorCode: error?.response?.data?.type || 'UNKNOWN_ERROR'
});
}
});
// Simple Health Check Endpoint
app.get('/health', (req, res) => {
res.status(200).json({ status: 'UP' });
});
// Start the Server
app.listen(PORT, () => {
console.log(`Server listening at http://localhost:${PORT}`);
console.log(`MMS Send Endpoint: POST http://localhost:${PORT}/send-mms`);
console.log(`Health Check: GET http://localhost:${PORT}/health`);
});
Explanation:
- Express Initialization:
const app = express()
creates an Express application instance. - Middleware:
express.json()
: Parses incoming requests with JSON payloads (like the one we'll send).express.urlencoded()
: Parses incoming requests with URL-encoded payloads.
- API Endpoint (
/send-mms
):- Defined using
app.post
to handle POST requests to/send-mms
. - Marked
async
because it calls ourasync sendMmsMessage
function. - Input Extraction: Destructures
recipientNumber
,imageUrl
, andcaption
from the request body (req.body
). - Input Validation: Includes basic checks:
- Ensures
recipientNumber
andimageUrl
are present. - Uses a simple regex (
/^\+?[1-9]\d{1,14}$/
) to check if therecipientNumber
looks like E.164 format. Note: For robust production validation, consider using a library likelibphonenumber-js
. - Checks if
imageUrl
is a valid URL format using theURL
constructor. - Returns a
400 Bad Request
status with an error message if validation fails.
- Ensures
- Calling Core Logic: Calls
sendMmsMessage
with the validated inputs. - Response Handling:
- On success, sends a
200 OK
status with a JSON response indicating success and including themessage_uuid
returned by Vonage. - On failure (catching errors from
sendMmsMessage
), sends a500 Internal Server Error
status with a generic error message. The detailed error is logged server-side (see Error Handling section).
- On success, sends a
- Defined using
- Health Check Endpoint (
/health
): A standard endpoint useful for monitoring tools to check if the service is running. Returns a simple200 OK
with a status message. - Server Start:
app.listen
starts the server on the configuredPORT
and logs confirmation messages.
Testing with curl
:
Once your .env
is configured and the server is running (node index.js
), you can test the endpoint from your terminal using curl
:
curl -X POST http://localhost:3000/send-mms \
-H "Content-Type: application/json" \
-d '{
"recipientNumber": "YOUR_RECIPIENT_PHONE_NUMBER",
"imageUrl": "https://placekitten.com/300/400",
"caption": "Test MMS from Express API"
}'
Replace:
YOUR_RECIPIENT_PHONE_NUMBER
with a real phone number (must be whitelisted if your Vonage account is in demo mode).(Optional)
https://placekitten.com/300/400
with the URL of the image you want to send.(Optional)
The caption text.
You should receive a JSON response like:
// Success Response
{
"success": true,
"message": "MMS send request accepted.",
"messageId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" // Actual message UUID
}
// Error Response (e.g., missing field)
{
"success": false,
"message": "Missing required fields: recipientNumber and imageUrl are required."
}
And check the recipient's phone for the MMS message!
4. Integrating with Necessary Third-Party Services (Vonage)
This section details how to obtain and configure the necessary credentials from Vonage.
1. Obtain API Key and Secret:
- Navigate to the Vonage API Dashboard.
- Your API Key and API Secret are displayed prominently at the top of the dashboard homepage.
- Action: Copy these values and paste them into your
.env
file forVONAGE_API_KEY
andVONAGE_API_SECRET
.
2. Create a Vonage Application:
- In the Vonage Dashboard, navigate to Applications > Create a new application.
- Give your application a descriptive Name (e.g., ""Node Express MMS Sender"").
- Click Generate public and private key. This will automatically:
- Populate the Public key field in the form.
- Trigger a download of the corresponding
private.key
file to your computer. Save this file securely.
- Action: Move the downloaded
private.key
file into your project's root directory (or updateVONAGE_PRIVATE_KEY_PATH
in.env
if you place it elsewhere). - Enable the Messages capability toggle.
- You'll be asked for an Inbound URL and a Status URL. For sending MMS only, these are less critical but still required by the dashboard. If you plan to receive status updates, you'll need a publicly accessible endpoint (e.g., using
ngrok
during development:https://YOUR_NGROK_ID.ngrok.io/webhooks/status
). For now, you can use a placeholder or a service like MockBin to generate temporary URLs that return a 200 OK. - Example using MockBin: Go to MockBin, click ""Create Bin"", keep defaults, click ""Create Bin"" again. Copy the generated URL (e.g.,
https://mockbin.org/bin/xxxxxxxx-xxxx...
) and paste it into both Inbound and Status URL fields.
- You'll be asked for an Inbound URL and a Status URL. For sending MMS only, these are less critical but still required by the dashboard. If you plan to receive status updates, you'll need a publicly accessible endpoint (e.g., using
- Scroll down and click Create Application.
- You will be redirected to the application's details page. Find the Application ID.
- Action: Copy the Application ID and paste it into your
.env
file forVONAGE_APPLICATION_ID
.
3. Purchase and Link an MMS-Capable Number:
- In the Vonage Dashboard, navigate to Numbers > Buy numbers.
- Search for numbers, ensuring you filter by:
- Country: United States (MMS is primarily a US feature for Vonage A2P).
- Features: Select SMS and MMS.
- Type: Typically Mobile.
- Choose a number and click Buy.
- Navigate to Numbers > Your numbers.
- Find the number you just purchased. Click the Link button (or Manage -> Link to Application) in the row for that number.
- Select the application you created in Step 2 (""Node Express MMS Sender"") from the dropdown list.
- Click Link.
- Action: Copy the purchased phone number (in E.164 format, e.g.,
+12015550123
) and paste it into your.env
file forVONAGE_MMS_FROM_NUMBER
.
4. Configure SDK (Recap):
Your index.js
already initializes the Messages
client using the environment variables:
// index.js (Relevant part)
const vonageClient = new Messages({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: readPrivateKey(process.env.VONAGE_PRIVATE_KEY_PATH),
});
Environment Variable Summary:
VONAGE_API_KEY
: Your main account API key (Dashboard).VONAGE_API_SECRET
: Your main account API secret (Dashboard).VONAGE_APPLICATION_ID
: ID of the specific Vonage Application you created (Application Details Page).VONAGE_PRIVATE_KEY_PATH
: Filesystem path to theprivate.key
file downloaded when creating the application (Saved locally).VONAGE_MMS_FROM_NUMBER
: The MMS-capable US number you purchased and linked to the application (Your Numbers Page). Format: E.164 (+1...
).PORT
: The local port your Express server will run on (User Defined).
By following these steps, you ensure your application has the necessary credentials and configuration to authenticate with and use the Vonage Messages API for sending MMS. Remember to keep your API Secret and Private Key secure.
5. Implementing Proper Error Handling, Logging, and Retry Mechanisms
Robust error handling and logging are crucial for production applications.
Error Handling Strategy:
Our current setup uses try...catch
blocks in both the sendMmsMessage
function and the API endpoint handler (/send-mms
).
sendMmsMessage
Function: Catches errors specifically from thevonageClient.send()
call. It logs the detailed error (often found inerror.response.data
for Axios-based errors from the SDK) and then rethrows the error. This allows the calling function (the API handler) to manage the HTTP response.- API Endpoint (
/send-mms
):- Catches errors originating from
sendMmsMessage
or other issues within the handler. - Logs the error server-side using
console.error
for debugging. - Sends a standardized JSON error response to the client with an appropriate HTTP status code (e.g.,
500 Internal Server Error
for Vonage issues,400 Bad Request
for validation errors). We avoid sending detailed internal error messages back to the client for security reasons.
- Catches errors originating from
Logging:
Currently, we are using console.log
, console.warn
, and console.error
. For production, a more structured logging library is recommended.
- Recommendation: Use libraries like Winston or Pino. These enable:
- Different log levels (debug, info, warn, error).
- Structured logging formats (e.g., JSON), which are easier for log aggregation tools to parse.
- Multiple transports (e.g., logging to files, databases, or external services like Datadog or Logstash).
Example (Conceptual - using Winston):
// Conceptual Winston Setup (replace console logging)
import winston from '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: [
new winston.transports.Console(), // Log to console
// Add file transport for production:
// new winston.transports.File({ filename: 'error.log', level: 'error' }),
// new winston.transports.File({ filename: 'combined.log' }),
],
});
// Replace console.log with logger.info, console.error with logger.error, etc.
// Example in catch block:
// } catch (error) {
// logger.error('API Error - Failed to send MMS:', {
// message: error.message,
// stack: error.stack,
// vonageError: error?.response?.data
// });
// res.status(500).json(/* ... */);
// }
Retry Mechanisms:
Network issues or temporary service outages can cause API calls to fail.
- Vonage Internal Retries: For certain transient errors, Vonage itself might perform retries when delivering messages. Refer to Vonage documentation for specifics.
- Client-Side Retries: You could implement retries within your
sendMmsMessage
function for specific error types (e.g., network timeouts, 5xx errors from Vonage).- Strategy: Use libraries like
async-retry
or implement a loop with exponential backoff (wait increasing amounts of time between retries) to avoid overwhelming the Vonage API. - Caution: Only retry idempotent operations (sending the same MMS twice should ideally have the same result, though it might result in duplicate messages if the first did succeed but the response failed). Be careful about which errors you retry. Retrying on validation errors (4xx) is usually pointless.
- Strategy: Use libraries like
Example (Conceptual - Basic Retry Logic):
// Conceptual Retry (Not fully implemented here)
async function sendMmsMessageWithRetry(recipientNumber, imageUrl, caption, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
// ... create messageRequest ...
const response = await vonageClient.send(messageRequest);
console.log(`MMS Sent Successfully (attempt ${i + 1}):`_ response);
return response;
} catch (error) {
console.error(`Attempt ${i + 1} failed:`_ error?.response?.data || error.message);
if (i === retries - 1) { // Last attempt failed
throw error; // Rethrow the final error
}
// Check if the error is retryable (e.g._ 5xx status code_ network error)
const isRetryable = error?.response?.status >= 500 || error.code === 'ETIMEDOUT'; // Example check
if (!isRetryable) {
throw error; // Don't retry non-retryable errors
}
// Exponential backoff: wait 1s, 2s, 4s...
const delay = Math.pow(2, i) * 1000;
console.log(`Retrying in ${delay / 1000}s...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// Remember to call this function from the API handler instead of sendMmsMessage
Testing Error Scenarios:
- Invalid Credentials: Temporarily modify
.env
with incorrect keys/secrets/IDs. - Invalid Recipient/Sender: Use incorrectly formatted numbers or a sender number not linked/MMS-capable.
- Invalid Image URL: Provide a non-existent or private URL.
- Network Issues: Simulate network failure (e.g., disconnect Wi-Fi briefly during a request, or use tools to throttle network).
- Vonage Demo Account Limits: If using a demo account, try sending to a non-whitelisted number (see Troubleshooting).
6. Creating a Database Schema and Data Layer
For the core functionality of sending an MMS message based on an immediate API request, a database is not strictly required. Our current implementation is stateless – it receives a request, calls the Vonage API, and returns a response without persisting data about the send request itself.
When Would a Database Be Needed?
You would introduce a database layer if you needed to:
- Track Sending History: Store records of each MMS sent (recipient, sender, image URL, timestamp, Vonage message ID, status).
- Manage Scheduled Messages: Store messages to be sent at a future time.
- Handle Asynchronous Processing: Store the request details in a database and have a separate worker process pick it up and send the MMS, allowing the API to respond faster.
- Store User Data: If the API were part of a larger application managing user accounts, preferences, etc.
- Correlate Status Updates: Store the
message_uuid
and associate it with application-specific data, then update the record when receiving delivery status webhooks from Vonage.
If a Database Were Added (Conceptual):
- Schema (e.g., PostgreSQL/MySQL):
CREATE TABLE mms_log ( id SERIAL PRIMARY KEY, vonage_message_uuid VARCHAR(255) UNIQUE, -- From Vonage response recipient_number VARCHAR(20) NOT NULL, sender_number VARCHAR(20) NOT NULL, image_url TEXT NOT NULL, caption TEXT, status VARCHAR(50) DEFAULT 'submitted', -- e.g., submitted, delivered, failed submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, last_updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, error_message TEXT -- Store error details if sending failed ); -- Index for efficient lookup by message ID CREATE INDEX idx_mms_log_message_uuid ON mms_log(vonage_message_uuid); -- Index for querying by recipient CREATE INDEX idx_mms_log_recipient ON mms_log(recipient_number);
- Data Layer (e.g., using an ORM like Prisma or Sequelize):
- Define a model corresponding to the
mms_log
table. - Implement functions to:
createMmsLogEntry(data)
: Insert a new record before or after attempting to send.updateMmsLogStatus(messageUuid, status, errorMessage)
: Update the status based on Vonage response or webhooks.
- Define a model corresponding to the
- Migrations: Use tools like
prisma migrate dev
orsequelize-cli db:migrate
to manage schema changes.
Conclusion for this Guide: Since the primary goal is the direct sending mechanism via API, we will proceed without a database layer to keep the example focused.
7. Adding Security Features
Securing your API is c