code examples
code examples
Send MMS Messages with Node.js and Fastify: Complete Vonage Integration Guide
Learn how to build a production-ready Node.js application using Fastify to send MMS multimedia messages via Vonage Messages API. Includes setup, security, deployment, and troubleshooting.
Dockerfile
This comprehensive guide walks you through building a production-ready Node.js application using the Fastify framework to send Multimedia Messaging Service (MMS) messages via the Vonage Messages API. We will cover everything from initial project setup and Vonage configuration to implementing the core sending logic, error handling, security best practices, deployment strategies, and verification.
By the end of this tutorial, you will have a robust API endpoint capable of accepting requests to send images as MMS messages to specified recipients within the United States.
Project Goals:
- Build a simple, performant API endpoint using Fastify.
- Integrate with the Vonage Messages API to send MMS messages.
- Implement proper configuration management using environment variables.
- Incorporate basic error handling, logging, and security measures.
- Provide clear steps for setup, deployment, and verification.
Technologies Used:
- Node.js: The runtime environment for our JavaScript application.
- Fastify: A high-performance, low-overhead web framework for Node.js, chosen for its speed, extensibility, and developer experience.
- Vonage Messages API: The service used to send MMS messages programmatically.
@vonage/messages: The official Vonage Node.js SDK for interacting with the Messages API.dotenv: A utility to load environment variables from a.envfile intoprocess.env.
Prerequisites:
- Node.js and npm (or yarn): Installed on your development machine. (Verify with
node -vandnpm -v). - Vonage API Account: Sign up if you don't have one. You get free credit to start.
- Vonage API Key and Secret: Found on your Vonage API Dashboard.
- A US Vonage Number: Purchase a US number capable of sending SMS and MMS through the Vonage dashboard (
Numbers>Buy Numbers). Note: MMS via the Messages API is currently supported for A2P use cases from US 10DLC, Toll-Free, and Short Code numbers to US destinations. - Vonage Application: You'll need to create a Messages API application in the Vonage dashboard. This process generates:
- Application ID: A unique identifier for your application.
- Private Key: A file (
private.key) used for JWT authentication (required for Messages API v1).
- Basic understanding of Node.js, APIs, and the command line.
System Architecture:
The basic flow of our application will be:
+--------+ +-----------------+ +-----------------------+ +-----------+
| Client |------>| Fastify API App |------>| Vonage Messages API |----->| Recipient |
| (e.g., | HTTP | (Node.js) | HTTPS | (api.nexmo.com) | MMS | (Mobile) |
| curl) | POST | | API | | | |
+--------+ +-----------------+ +-----------------------+ +-----------+
| |
| Reads .env file |
| Uses Vonage SDK |
+-----------------+
1. Setting Up Your Node.js Project for MMS Sending
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.
bashmkdir fastify-vonage-mms cd fastify-vonage-mms -
Initialize Node.js Project: Create a
package.jsonfile. The-yflag accepts default settings.bashnpm init -y -
Install Dependencies: Install Fastify, the Vonage Messages SDK, and
dotenv.bashnpm install fastify @vonage/messages dotenv -
Create Project Structure: Set up a basic file structure.
bashtouch server.js .env .gitignoreserver.js: This will contain our Fastify application code..env: This file will store our sensitive credentials and configuration (API keys, numbers, etc.). 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.plaintext# .gitignore node_modules .env *.log private.keyWhy
.gitignore? It prevents sensitive data (in.envandprivate.key) and large dependency folders (node_modules) from being accidentally tracked by Git and pushed to repositories like GitHub.
2. Integrating with Vonage: Application Setup and Configuration
Before writing code, we need to configure our Vonage account and application correctly. This is crucial for authentication, especially for the Messages API.
Vonage requires webhook URLs to receive delivery status updates and inbound messages. Even for sending-only applications, you need to provide these endpoints during setup.
-
Navigate to Vonage Dashboard: Log in to your Vonage API Dashboard.
-
Create a New Application:
- Go to
Applications>Create a new application. - Give your application a name (e.g.,
Fastify MMS Sender). - Enable
MessagesCapability: Toggle this capability on. - Webhook URLs (Inbound and Status): The Messages API requires webhook URLs even if you only plan to send outbound messages. For this simple guide, you can use a placeholder service like Mockbin:
- Go to Mockbin.com and click
Create Bin. - Leave the defaults (Status Code 200 OK) and click
Create Bin. - Copy the generated URL (e.g.,
https://mockbin.org/bin/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). - Paste this URL into both the
Inbound URLandStatus URLfields under the Messages capability in the Vonage dashboard. In a production scenario, these would point to endpoints on your server to receive delivery receipts and inbound messages.
- Go to Mockbin.com and click
- Generate Public/Private Key Pair: Click the
Generate public/private key pairlink. This will:- Populate the
Public keyfield in the dashboard. - Automatically download a file named
private.keyto your computer. Move thisprivate.keyfile into your project's root directory (fastify-vonage-mms/).
- Populate the
- Click
Create a new application.
- Go to
-
Link Your Vonage Number:
- After creating the application, you'll be taken to its configuration page. Scroll down to
Link numbers. - Find the US MMS-capable number you purchased earlier and click the
Linkbutton next to it. This step is essential – the API will reject requests from numbers not linked to the Application ID being used.
- After creating the application, you'll be taken to its configuration page. Scroll down to
-
Collect Credentials: Note down the following from your dashboard:
- API Key & API Secret: Found at the top of the main Dashboard page.
- Application ID: Found on the settings page of the application you just created.
- Your Vonage Number (FROM_NUMBER): The US number you linked to the application (use E.164 format, e.g.,
14155550100).
3. Configuring Environment Variables for Secure API Access
We use environment variables to manage configuration and keep sensitive credentials out of the codebase.
-
Edit the
.envfile: Open the.envfile in your editor and add the following variables, replacing the placeholder values with your actual credentials obtained in the previous step.dotenv# .env # Vonage API Credentials (From Dashboard Home) VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Vonage Application Credentials (From Application Settings) VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID VONAGE_APPLICATION_PRIVATE_KEY_PATH=./private.key # Vonage Number (Must be linked to Application ID, E.164 format) VONAGE_FROM_NUMBER=14155550100 # Server Configuration PORT=3000 HOST=0.0.0.0 -
Explanation of Variables:
VONAGE_API_KEY,VONAGE_API_SECRET: Your main account credentials. Used by the SDK for some underlying authentication checks.VONAGE_APPLICATION_ID: Identifies the specific Vonage application configuration to use (linking, keys, webhooks). Essential for Messages API v1.VONAGE_APPLICATION_PRIVATE_KEY_PATH: The relative path fromserver.jsto your downloadedprivate.keyfile. The SDK uses this to generate JWTs for authenticating Messages API requests.VONAGE_FROM_NUMBER: The Vonage number (in E.164 format) that will send the MMS. Must be linked to theVONAGE_APPLICATION_ID.PORT: The port your Fastify server will listen on.HOST: The host address the server will bind to.0.0.0.0makes it accessible from outside localhost (useful in containers or VMs).
4. Implementing the Fastify API Endpoint for Sending MMS
Now, let's write the code for our Fastify server and the MMS sending endpoint.
server.js
// server.js
// 1. Load Environment Variables
require('dotenv').config();
// 2. Import Dependencies
const Fastify = require('fastify');
const { Messages, MMSImage } = require('@vonage/messages');
// 3. Initialize Fastify
// Enable logging for better debugging
const fastify = Fastify({
logger: {
level: 'info', // Log informational messages and above
transport: {
target: 'pino-pretty', // Make logs more readable during development
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
}
});
// 4. Initialize Vonage Messages Client
// Ensure all required environment variables are present
const requiredEnv = [
'VONAGE_API_KEY',
'VONAGE_API_SECRET',
'VONAGE_APPLICATION_ID',
'VONAGE_APPLICATION_PRIVATE_KEY_PATH',
'VONAGE_FROM_NUMBER'
];
const missingEnv = requiredEnv.filter(envVar => !process.env[envVar]);
if (missingEnv.length > 0) {
console.error(`Error: Missing required environment variables: ${missingEnv.join(', ')}`);
console.error("Please check your .env file or environment configuration.");
process.exit(1); // Exit if configuration is incomplete
}
const vonageMessages = new Messages({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_APPLICATION_PRIVATE_KEY_PATH, // SDK reads the file content
});
// 5. Define the MMS Sending Route Schema (for Validation)
const sendMmsSchema = {
body: {
type: 'object',
required: ['to', 'imageUrl'], // 'to' and 'imageUrl' are mandatory
properties: {
to: {
type: 'string',
description: 'Recipient phone number, ideally in E.164 format (e.g., +14155550101 or 14155550101 for US)',
// Pattern checks for optional '+' followed by 1-15 digits (non-zero first digit)
pattern: '^\\+?[1-9]\\d{1,14}$'
},
imageUrl: {
type: 'string',
description: 'Publicly accessible URL of the image (.jpg, .jpeg, .png)',
format: 'url' // Check if it looks like a URL
},
caption: {
type: 'string',
description: 'Optional text caption for the image',
maxLength: 1000 // Example max length, check Vonage docs for specifics
}
},
},
response: { // Define expected response structures
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
messageId: { type: 'string' },
message: { type: 'string' }
}
},
400: { // Bad Request (Validation Failed)
type: 'object',
properties: {
success: { type: 'boolean', default: false },
error: { type: 'string' },
details: { type: 'object' } // Include validation details
}
},
500: { // Server Error (e.g., Vonage API issue)
type: 'object',
properties: {
success: { type: 'boolean', default: false },
error: { type: 'string' }
}
}
}
};
// 6. Implement the API Endpoint (/send-mms)
fastify.post('/send-mms', { schema: sendMmsSchema }, async (request, reply) => {
const { to, imageUrl, caption } = request.body; // Destructure validated body
const from = process.env.VONAGE_FROM_NUMBER;
fastify.log.info(`Attempting to send MMS from ${from} to ${to} with image: ${imageUrl}`);
try {
// Construct the MMS payload using the Vonage SDK
const mmsPayload = new MMSImage({
to: to,
from: from,
image: {
url: imageUrl,
caption: caption || '', // Use caption if provided, else empty string
},
// Optional: client_ref for tracking
// client_ref: `my-internal-id-${Date.now()}`
});
// Send the message via Vonage API
const response = await vonageMessages.send(mmsPayload);
fastify.log.info(`MMS submitted successfully to Vonage. Message UUID: ${response.message_uuid}`);
// Send success response back to the client
reply.code(200).send({
success: true,
messageId: response.message_uuid,
message: 'MMS submitted successfully.'
});
} catch (error) {
fastify.log.error(`Error sending MMS to ${to}: ${error.message}`);
// Log the full error object for detailed debugging if needed
// fastify.log.error(error);
let errorMessage = 'Failed to send MMS.';
let statusCode = 500;
// Check for specific Vonage API error details if available
if (error.response && error.response.data) {
fastify.log.error(`Vonage API Error Details: ${JSON.stringify(error.response.data)}`);
errorMessage = `Vonage API Error: ${error.response.data.title || error.message}`;
if (error.response.status === 401) {
errorMessage = "Vonage authentication failed. Check API Key/Secret, Application ID, Private Key, and linked number.";
statusCode = 401; // Reflect the actual error status
} else if (error.response.status === 400) {
errorMessage = `Vonage Bad Request: ${error.response.data.detail || error.response.data.title || 'Check parameters.'}`;
statusCode = 400; // It might be a client-side parameter issue
} else {
statusCode = error.response.status || 500; // Use Vonage status if available
}
}
// Send error response back to the client
reply.code(statusCode).send({
success: false,
error: errorMessage
});
}
});
// 7. Add a simple Health Check endpoint
fastify.get('/health', async (request, reply) => {
return { status: 'ok', timestamp: new Date().toISOString() };
});
// 8. Start the Fastify Server
const start = async () => {
try {
const port = parseInt(process.env.PORT || '3000', 10);
const host = process.env.HOST || '0.0.0.0';
await fastify.listen({ port: port, host: host });
fastify.log.info(`Server listening on http://${host}:${port}`);
} catch (err) {
fastify.log.error('Error starting server:', err);
process.exit(1);
}
};
start();Code Explanation:
- Load Environment Variables:
require('dotenv').config()loads the variables from your.envfile intoprocess.env. This must be done early. - Import Dependencies: Bring in
fastifyand the specific classes needed from@vonage/messages. - Initialize Fastify: Create a Fastify instance. We enable
loggerfor better visibility during development and production.pino-prettymakes logs easier to read locally. - Initialize Vonage Client: We check if all necessary Vonage-related environment variables are set. If not, the application logs an error and exits. Then, we instantiate the
Messagesclient using the credentials fromprocess.env. The SDK handles reading theprivate.keyfile content. - Route Schema: Fastify's built-in JSON Schema validation is used. We define the expected structure (
body) and constraints (required,type,pattern,format,maxLength) for incoming requests to/send-mms. We also define the structure of potential success (200) and error (400,500) responses. This provides automatic request validation and response serialization. Thepatternfortovalidates numbers generally conforming to E.164 format (optional+, up to 15 digits). - API Endpoint (
/send-mms):fastify.post('/send-mms', { schema: sendMmsSchema }, ...)defines a POST route. Theschemaoption automatically validates incoming requests againstsendMmsSchema.body. If validation fails, Fastify sends a 400 Bad Request response automatically.- Inside the
asynchandler, we destructure the validatedrequest.body. - We construct the
MMSImagepayload, passing theto,from(from env), andimagedetails (URL and optional caption). vonageMessages.send()makes the actual API call to Vonage.- A
try...catchblock handles potential errors during the API call. - On success, we log the
message_uuidreturned by Vonage and send a 200 OK response with the UUID. - On failure, we log the error and send an appropriate error response (using the status code from the Vonage error if available, otherwise 500 Internal Server Error). We try to provide more specific error messages based on common Vonage issues (like 401 Unauthorized).
- Health Check: A standard
/healthendpoint that returns a 200 OK status, useful for monitoring systems to check if the service is running. - Start Server: The
startfunction usesfastify.listen()to bind the server to the specified host and port (from.envor defaults). Errors during startup are caught and logged.
5. Error Handling and Logging Best Practices
- Logging: Fastify's built-in Pino logger is configured for
infolevel. Errors are explicitly logged in thecatchblock usingfastify.log.error(). In production, you might configure different log levels or transports (e.g., sending logs to a centralized service). - Request Validation: Fastify's schema validation handles invalid input formats before our route handler code runs, returning informative 400 errors.
- API Errors: The
try...catchblock handles errors from thevonageMessages.send()call. We attempt to parse common Vonage error responses (error.response.data,error.response.status) to provide more helpful feedback to the client. - Retry Mechanisms: This guide doesn't implement automatic retries. For production, consider adding a retry mechanism (e.g., using libraries like
async-retryorp-retry) with exponential backoff for transient network issues or temporary Vonage API unavailability when callingvonageMessages.send(). However, be cautious retrying 4xx errors (client errors) as they likely won't succeed without changes.
6. Database Schema and Data Layer
This specific guide focuses solely on the stateless action of sending an MMS and does not require a database.
If you needed to track message status, store sending history, or manage user data, you would:
- Choose a Database: PostgreSQL, MySQL, MongoDB, etc.
- Design Schema: Define tables/collections (e.g.,
messagestable withmessage_uuid,to_number,from_number,status,image_url,timestamp,vonage_response). - Implement Data Layer: Use an ORM (like Prisma, Sequelize, TypeORM) or a database client library (like
pg,mysql2) to interact with the database. - Integrate: Save message details before sending to Vonage, and update the status based on the API response or status webhooks.
Adding a database is beyond the scope of this initial MMS sending guide.
7. Adding Security Features to Your MMS Application
-
Input Validation: Handled robustly by Fastify's schema validation, preventing unexpected data types or missing fields. The pattern matching for
toandformat: 'url'forimageUrladd further checks. Always sanitize or validate any data before using it. -
Credential Security: Sensitive keys are stored in
.envand kept out of version control via.gitignore. Ensure theprivate.keyfile has restrictive file permissions on the server (e.g.,chmod 400 private.key). In production, use a dedicated secret management system (like HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) instead of.envfiles. -
Rate Limiting: Protects against abuse and brute-force attempts. Install and configure
fastify-rate-limit:bashnpm install fastify-rate-limitAdd this registration near the top of
server.js, after initializing Fastify:javascript// server.js (near top) fastify.register(require('fastify-rate-limit'), { max: 100, // Max requests per windowMs per IP timeWindow: '1 minute' // Time window });Adjust
maxandtimeWindowbased on expected traffic. -
HTTPS: In production, always run your Node.js application behind a reverse proxy (like Nginx or Caddy) that handles TLS/SSL termination (HTTPS). Do not handle TLS directly in Node.js unless necessary.
-
Image URL Validation: Be cautious about the
imageUrlprovided. A malicious user could provide a URL pointing to an internal resource or attempt SSRF. While this guide assumes public URLs, production systems might need stricter validation, domain whitelisting, or fetching the image server-side first. -
Helmet: Consider using
@fastify/helmetto set various security-related HTTP headers.bashnpm install @fastify/helmetjavascript// server.js (near top) fastify.register(require('@fastify/helmet'));
8. Handling Special Cases and Edge Scenarios
- Number Formatting: The Vonage API typically expects numbers in E.164 format (e.g.,
+14155550100). The schema includes a basic pattern (^\\+?[1-9]\\d{1,14}$) that accepts this format, as well as the format without the+(e.g.,14155550100) which Vonage often accepts for US numbers. For maximum compatibility, sending in E.164 format is recommended. Robust validation might involve a dedicated library likelibphonenumber-js. - Image URL Accessibility: The
imageUrlmust be publicly accessible over the internet for Vonage to fetch it. Internal URLs or URLs requiring authentication will fail. - Supported Image Types: Vonage Messages API for MMS primarily supports
.jpg,.jpeg, and.png. Sending other types may result in errors or unexpected behavior. The maximum file size limits should also be considered (check Vonage documentation). - Captions: Character limits exist for captions. Check the latest Vonage documentation for specifics. The schema adds a basic
maxLength. - Vonage Number Capabilities: Ensure the
VONAGE_FROM_NUMBERis actually enabled for MMS sending in the US. Check the number's capabilities in the Vonage dashboard. - A2P Compliance: Remember MMS is for Application-to-Person use cases and subject to carrier filtering and compliance rules (like 10DLC registration for local numbers).
9. Implementing Performance Optimizations
-
Fastify's Speed: Fastify is inherently fast due to its architecture and optimized JSON handling.
-
Asynchronous Operations: The use of
async/awaitensures Node.js's event loop is not blocked during the Vonage API call. -
Logging: While essential, excessive synchronous logging in high-traffic scenarios can impact performance. Pino is designed to be fast, but be mindful.
-
Payload Size: Keep request/response payloads reasonably sized.
-
Caching: Not directly applicable for the sending action itself, but if you were fetching data to decide whether to send an MMS, caching that data (e.g., using Redis with
fastify-redis) could improve performance. -
Load Testing: Use tools like
k6,autocannon, orwrkto test the endpoint's performance under load and identify bottlenecks.bash# Example using autocannon (install with: npm install -g autocannon) # Note the use of single quotes for the JSON body to avoid shell interpretation issues autocannon -m POST -H 'Content-Type: application/json' -b '{"to": "14155550101", "imageUrl": "https://placekitten.com/200/300", "caption": "Test Cat"}' http://127.0.0.1:3000/send-mms
10. Adding Monitoring, Observability, and Analytics
- Health Checks: The
/healthendpoint provides a basic check for uptime monitoring tools (like Pingdom, UptimeRobot, or Kubernetes liveness probes). - Logging: Centralized logging (ELK stack, Datadog Logs, Grafana Loki) is crucial in production to aggregate logs from multiple instances. Structure your logs (JSON format helps) for easier parsing. Fastify/Pino logs in JSON by default when not using
pino-pretty. - Metrics: Instrument your application to collect key metrics:
- Request rate and duration (Fastify plugins like
fastify-metricscan help). - Error rates (track 4xx and 5xx responses).
- Vonage API call latency and success/error rates.
- Node.js process metrics (CPU, memory, event loop lag - e.g., using
prom-client). - Export these metrics to a system like Prometheus and visualize them in Grafana or Datadog.
- Request rate and duration (Fastify plugins like
- Error Tracking: Use services like Sentry (
@sentry/node) or Datadog APM to capture, aggregate, and alert on runtime errors with stack traces and context. - Distributed Tracing: For more complex microservice architectures, implement distributed tracing (OpenTelemetry, Jaeger, Zipkin) to follow requests across service boundaries.
11. Troubleshooting Common MMS Issues
401 UnauthorizedError: This is common with the Messages API.- Check: API Key/Secret (
.env). - Check: Application ID (
.env). - Check:
private.keyfile path (.env) and ensure the file exists and is readable by the Node.js process. - Check: Ensure the
VONAGE_FROM_NUMBERis correctly linked to theVONAGE_APPLICATION_IDin the dashboard. - Check: Account permissions – ensure your Vonage account/subaccount has Messages API enabled.
- Check: API Key/Secret (
- Invalid
fromNumber: EnsureVONAGE_FROM_NUMBERis correct, in E.164 format (or the format expected), and linked to the Application ID. - Invalid
toNumber: Ensure the recipient number is valid and in the expected format. Check Vonage logs for specific errors. Check schema validation pattern (^\\+?[1-9]\\d{1,14}$). Image URL cannot be retrieved/ Fetch Errors:- Verify the
imageUrlis publicly accessible (try opening it in an incognito browser window). - Check for typos in the URL.
- Ensure the image format is supported (
.jpg,.jpeg,.png). - Check Vonage status page for potential issues retrieving media.
- Verify the
- MMS Not Received:
- Check Vonage Dashboard Logs: Go to Logs > Messages API logs for detailed delivery status and error codes from carriers.
- Carrier Filtering: MMS can be subject to carrier filtering. Ensure compliance with regulations.
- Device Issues: The recipient device might have issues receiving MMS (data off, unsupported device).
- Use Delivery Receipts: Implement the Status Webhook URL properly to receive delivery status updates programmatically.
- Schema Validation Errors (400 Bad Request): Check the
detailsfield in the error response – Fastify provides specific information about which validation rule failed (e.g., missing required field, incorrect type, pattern mismatch). - Rate Limiting Errors (429 Too Many Requests): You've exceeded the rate limit configured in
fastify-rate-limitor potentially Vonage's own API rate limits. .envNot Loading: Ensurerequire('dotenv').config();is called before accessingprocess.envvariables. Check the.envfile is in the correct directory relative to where you runnode server.js.- Private Key Permissions: On Linux/macOS, if the Node.js process cannot read the
private.keyfile due to permissions, you'll likely get an authentication error. Ensure the user running the Node process has read access.
12. Deployment and CI/CD Strategies
Basic Deployment (using PM2):
PM2 is a process manager for Node.js that helps keep your application alive and manage clustering.
-
Install PM2 Globally:
bashnpm install pm2 -g -
Create Ecosystem File (
ecosystem.config.js): This file defines how PM2 should run your app.javascript// ecosystem.config.js module.exports = { apps : [{ name : "fastify-mms-sender", script : "./server.js", instances: "max", // Or a specific number like 2 exec_mode: "cluster", // Enable clustering for better performance watch : false, // Set to true to restart on file changes (dev only) env : { // Environment variables PM2 will set NODE_ENV: "production", // PORT, HOST, VONAGE vars can also be set here, // but using system env variables or secret management is better } }] } -
Upload Code: Transfer your project files (
server.js,package.json,package-lock.json,ecosystem.config.js,private.key) to your server. Do NOT upload.env. -
Install Dependencies on Server:
bashnpm install --omit=dev # Install only production dependencies -
Set Environment Variables: Configure the required environment variables (
VONAGE_API_KEY,VONAGE_API_SECRET, etc.) directly on the server environment or use a secret management tool. Do not rely on the.envfile in production. -
Start Application with PM2:
bashpm2 start ecosystem.config.js -
Manage Application:
pm2 list: Show running processes.pm2 logs fastify-mms-sender: View logs.pm2 stop fastify-mms-sender: Stop the app.pm2 restart fastify-mms-sender: Restart the app.pm2 save: Save the current process list for reboot persistence.pm2 startup: Generate command to run on system boot.
Containerization (Docker):
Create a Dockerfile to package your application.
# Dockerfile
# Use an official Node.js runtime as a parent image
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files and install dependencies
COPY package*.json ./
RUN npm install --omit=dev
# Copy the rest of the application code
COPY . .
# --- Final Stage ---
FROM node:18-alpine
WORKDIR /app
# Copy only necessary files from the builder stage
COPY /app/node_modules ./node_modules
COPY /app/server.js ./server.js
COPY /app/private.key ./private.key # Copy private key
# Expose the port the app runs on
EXPOSE 3000
# Define environment variables (can be overridden at runtime)
# Best practice: Provide these via docker run -e or orchestration tools
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
# ENV VONAGE_API_KEY=... (Set these at runtime!)
# ENV VONAGE_API_SECRET=...
# ENV VONAGE_APPLICATION_ID=...
ENV VONAGE_APPLICATION_PRIVATE_KEY_PATH=./private.key
# ENV VONAGE_FROM_NUMBER=...
# Command to run the application
CMD [ "node", "server.js" ]Security Note: The Dockerfile above copies the private.key directly into the image. While simple, this is generally not recommended for production environments as it bundles a sensitive secret within the image artifact. Preferable approaches include:
- Volume Mounting: Mount the
private.keyfile from the host or a secure volume into the container at runtime. - Orchestration Secrets: Use secret management features provided by your orchestrator (e.g., Kubernetes Secrets, Docker Swarm Secrets, AWS Secrets Manager, GCP Secret Manager) to inject the key file or its content as an environment variable or file at runtime.
Build and run the container, passing environment variables securely.
CI/CD (Conceptual Example - GitHub Actions):
Create a workflow file .github/workflows/deploy.yml:
# .github/workflows/deploy.yml
name: Deploy MMS Sender
on:
push:
branches:
- main # Trigger deployment on push to main branch
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install --omit=dev
# Add steps here to:
# 1. Build (if needed, e.g., TypeScript compilation)
# 2. Test (npm test)
# 3. Package (e.g., build Docker image, create zip archive)
# 4. Deploy (e.g., push Docker image, ssh to server and restart pm2, deploy to cloud platform)
# Example: SSH and restart PM2 (requires setting up secrets for SSH keys/host)
# - name: Deploy to Server
# uses: appleboy/ssh-action@master
# with:
# host: ${{ secrets.SSH_HOST }}
# username: ${{ secrets.SSH_USERNAME }}
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# script: |
# cd /path/to/your/app
# git pull origin main
# npm install --omit=dev
# pm2 restart fastify-mms-sender || pm2 start ecosystem.config.js
# pm2 save13. Testing Your MMS Endpoint
After deploying your application, verify it's working correctly:
Using curl:
curl -X POST http://localhost:3000/send-mms \
-H "Content-Type: application/json" \
-d '{
"to": "14155550101",
"imageUrl": "https://placekitten.com/400/300",
"caption": "Hello from Fastify!"
}'Expected Success Response:
{
"success": true,
"messageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"message": "MMS submitted successfully."
}Verification Steps:
- Check the response status code is 200
- Verify the
messageIdis present in the response - Check the recipient device for the MMS message
- Review Vonage Dashboard logs for delivery status
- Test the
/healthendpoint to confirm the server is running
For more information on E.164 phone number formatting, see the international standard specification.
Related Resources
Frequently Asked Questions
How to send MMS messages with Node.js and Fastify?
Use the Vonage Messages API with the Fastify framework and the @vonage/messages SDK. This setup allows you to create a performant API endpoint to handle MMS sending requests within your Node.js application. The tutorial provides step-by-step instructions for setting up the project, configuring Vonage, and implementing the sending logic within a Fastify route.
What is the Vonage Messages API used for?
The Vonage Messages API enables sending various message types programmatically, including MMS. This tutorial focuses on sending image-based MMS messages to recipients in the US. It uses the @vonage/messages Node.js SDK to interact with the API, which requires specific credentials and configurations within your Vonage account.
Why use Fastify for sending MMS messages?
Fastify is a high-performance web framework for Node.js, chosen for its speed and developer-friendly experience. Its efficient routing and schema validation make it well-suited for building robust API endpoints, essential for reliable MMS sending. Its built-in JSON Schema validation adds further efficiency.
When should I use environment variables in a Node.js project?
Always use environment variables for sensitive credentials and configurations (API keys, secrets, server ports). Store these in a .env file locally, but *never* commit this file to version control. In production, employ dedicated secret management systems instead.
Can I send MMS messages internationally using this tutorial's setup?
The provided setup focuses on sending MMS messages within the United States using US Vonage numbers. International MMS may have different requirements or limitations; consult the Vonage Messages API documentation for details on supported destinations and compliance considerations.
How to set up a Vonage application for MMS?
In the Vonage API Dashboard, create a new application, enable the 'Messages' capability, provide webhook URLs (even placeholder ones), and generate a public/private key pair. Link your US Vonage number to the application, then collect your API Key, Secret, Application ID, and number.
What is the purpose of the private.key file?
The private.key file is crucial for authenticating with the Vonage Messages API. It is used by the @vonage/messages SDK to generate JWTs (JSON Web Tokens), which are required for secure communication with the API. Keep this file secure and never commit it to version control.
How to handle errors when sending MMS with Vonage?
Implement a try...catch block around the vonageMessages.send() call to handle errors during the API request. Log errors for debugging, and provide informative error responses to the client. Consider adding retry mechanisms with exponential backoff for transient errors.
What image formats are supported for MMS via Vonage?
The Vonage Messages API primarily supports .jpg, .jpeg, and .png image formats for MMS. Ensure your image URLs point to publicly accessible files of these supported types. Verify Vonage specifications for maximum file sizes.
How do I secure my Fastify MMS API endpoint?
Use Fastify's JSON Schema validation, manage credentials securely (environment variables or dedicated secret management systems), implement rate limiting (e.g., with fastify-rate-limit), use HTTPS, and protect against insecure image URLs or SSRF (Server-Side Request Forgery) attacks.
What is the role of the .gitignore file?
The .gitignore file specifies files and directories that Git should ignore, preventing them from being tracked and accidentally committed. It's essential to add node_modules, .env, private.key, and log files to .gitignore to protect sensitive information and keep your repository clean.
How to troubleshoot a 401 Unauthorized error with the Vonage Messages API?
Verify your API Key, Secret, Application ID, private.key file path, and ensure the Vonage number is linked to the application in the dashboard. Check your Vonage account permissions and confirm MMS capabilities are enabled.
What should I do if the MMS message is not received by the recipient?
Check Vonage Dashboard logs for delivery status, ensure compliance with carrier filtering rules, verify recipient device capabilities, and consider implementing status webhooks to track message delivery programmatically.
How can I improve the performance of my Fastify MMS sending application?
Leverage Fastify's speed, use asynchronous operations (async/await), optimize logging practices, and consider caching relevant data if applicable. Conduct load testing (with tools like autocannon or k6) to identify potential bottlenecks.
How to deploy a Fastify Node.js application for MMS sending?
Use a process manager like PM2 for robust process management and clustering. Containerize the application with Docker for portability and scalability. Implement CI/CD pipelines (e.g., with GitHub Actions) to automate testing, building, and deployment.