Send MMS with Fastify and Node.js Using Infobip API: Complete Guide
Learn how to send MMS messages using Fastify, Node.js, and the Infobip API. Complete tutorial with code examples, error handling, and deployment best practices for multimedia messaging.
This comprehensive guide shows you how to build a production-ready Node.js application using the Fastify framework to send Multimedia Messaging Service (MMS) messages via the Infobip API. Whether you're adding multimedia messaging capabilities to an existing application or building a new messaging service, this tutorial covers everything from initial project setup and API configuration to error handling, security best practices, and deployment strategies.
By the end of this tutorial, you'll have a fully functional Fastify API endpoint capable of accepting MMS details (recipient, sender, media URL, caption) and relaying them through Infobip's messaging infrastructure for reliable delivery. This implementation solves the common need to programmatically integrate rich media messaging into applications without manual intervention.
Project Overview and Goals
Goal: Build a simple but robust API endpoint using Fastify to send MMS messages via Infobip.
Problem Solved: Programmatically send images and captions as MMS messages without needing manual intervention through a web interface.
Technologies:
Node.js: The runtime environment for our JavaScript backend.
Fastify: A high-performance, low-overhead web framework for Node.js, chosen for its speed, extensibility, and developer experience. This guide uses Fastify v4.x; Fastify v5 is also available and follows similar patterns.
Infobip API: The third-party service used for sending MMS messages.
@infobip-api/sdk: The official Infobip Node.js SDK, simplifying interaction with their API.
dotenv: For managing environment variables securely.
Outcome: A Fastify server with a POST /send-mms endpoint that takes JSON payload and sends an MMS message using credentials stored in environment variables.
Prerequisites:
Node.js v18, v20, or v22 (LTS versions) and npm (or yarn) installed. Note: Node.js v14 reached end-of-life on April 30, 2023.
Note: Free trial accounts often have limitations, such as only being able to send messages to the phone number used during registration.
Your Infobip API Key and Base URL. Find these in your Infobip account dashboard.
A publicly accessible URL for the media file (e.g., image) you want to send. Infobip needs to fetch the media from this URL. Supported formats typically include JPEG, PNG, and GIF; verify current supported formats in the Infobip MMS API documentation.
System Architecture:
Code
+-------------+ +-----------------+ +-------------------+ +-------------+
| |--(1)-->| |----->| |----->| |
| User/Client | | Fastify API | (2) | Infobip Service | (3) | Infobip API |
| (e.g. curl) |<--(5)--| (/send-mms) |<-----| (using SDK) |<-----| |
| | | | (4) | | | |
+-------------+ +-----------------+ +-------------------+ +-------------+
1. Client sends POST request with MMS details (to, from, mediaUrl, caption) to Fastify.
2. Fastify route handler validates input and calls the Infobip service module.
3. Infobip service module uses the SDK to format the request and sends it to the Infobip API endpoint.
4. Infobip API processes the request and returns a response (success/failure) to the SDK/service.
5. Fastify API relays the success or error response back to the client.
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.
Bash
mkdir fastify-infobip-mms
cd fastify-infobip-mms
Initialize Node.js Project:
Initialize the project using npm. The -y flag accepts the default settings.
Bash
npm init -y
This creates a package.json file.
Install Dependencies:
We need Fastify for the web server, the Infobip SDK to interact with their API, and dotenv to manage environment variables.
Bash
npm install fastify @infobip-api/sdk dotenv
Create Project Structure:
Set up a basic directory structure for organization:
Text
fastify-infobip-mms/
├── node_modules/
├── services/
│ └── infobipService.js # Logic for interacting with Infobip
├── .env # Local environment variables (DO NOT COMMIT)
├── .env.example # Example environment variables (Commit this)
├── .gitignore # Specify files/folders to ignore in Git
├── package.json
├── package-lock.json
└── server.js # Main Fastify application file
Configure .gitignore:
Create a .gitignore file in the project root and add node_modules and .env to prevent committing them to version control.
Text
# .gitignore
node_modules
.env
Set up Environment Variables:
Create two files in the root directory: .env (for your actual secrets) and .env.example (as a template).
.env:
Copy .env.example to .env and replace the placeholder values with your actual Infobip API Key and Base URL obtained from your Infobip dashboard. Set the desired port.
Purpose: Using environment variables keeps sensitive credentials out of your codebase and enables different configurations for development, staging, and production environments. dotenv loads these variables into process.env for easy access in your Node.js application during development.
Now, let's create the service module responsible for handling communication with the Infobip API using their SDK.
Create infobipService.js:
Inside the services directory, create the infobipService.js file.
Implement the Service:
Add the following code to services/infobipService.js.
JavaScript
// services/infobipService.js'use strict';
const { Infobip, AuthType } = require('@infobip-api/sdk');
// Check for essential environment variables during module load.// In a production application, this check is often better placed in the main// application entry point (server.js) before server initialization,// allowing for a more controlled shutdown or error reporting.if (!process.env.INFOBIP_API_KEY || !process.env.INFOBIP_BASE_URL) {
console.error('Error: Missing Infobip API Key or Base URL in environment variables.');
// Throwing an error here will typically stop the application startup process.thrownewError('Missing Infobip API Key or Base URL in environment variables.');
}
// Initialize the Infobip clientconst infobip = newInfobip({
baseUrl: process.env.INFOBIP_BASE_URL,
apiKey: process.env.INFOBIP_API_KEY,
authType: AuthType.ApiKey, // Specify API Key authentication
});
/**
* Sends an MMS message using the Infobip API.
* @param {string} to - The recipient's phone number in E.164 format (e.g., +14155552671).
* @param {string} from - Your Infobip MMS-enabled sender number in E.164 format.
* @param {string} mediaUrl - The publicly accessible URL of the media file (image).
* @param {string} [caption] - Optional caption text for the media.
* @returns {Promise<object>} - The response object from the Infobip API.
* @throws {Error} - Throws an error if the API call fails.
*/asyncfunctionsendMms(to, from, mediaUrl, caption) {
console.log(`Attempting to send MMS: to=${to}, from=${from}, mediaUrl=${mediaUrl}`); // Basic logging// Construct the MMS payload according to Infobip SDK/API requirementsconst mmsPayload = {
from: from,
to: to,
content: {
mediaUrl: mediaUrl,
// caption: caption || '', // Include caption if provided// mediaType: 'IMAGE' // You might need to specify this depending on SDK version/API behavior
},
// Optional: Add other parameters like delivery reports callback URL etc.// notifyUrl: 'YOUR_DELIVERY_REPORT_WEBHOOK_URL'
};
// Add caption only if it existsif (caption) {
mmsPayload.content.caption = caption;
}
try {
// Use the Infobip SDK to send the MMS message via the 'mms' channel// The specific method might vary slightly based on SDK updates, check SDK docs.// Assuming a method like `infobip.channels.mms.send(payload)` exists.// Referencing potential structure based on SDK patterns. The MMS API// might expect messages in an array even for single sends. Verify this structure.const response = await infobip.channels.mms.send({
messages: [mmsPayload]
});
console.log('Infobip API Response:', JSON.stringify(response, null, 2));
return response; // Return the successful response body
} catch (error) {
console.error('Error sending MMS via Infobip:', error.message);
// Log the full error if available (might contain more details from Infobip)if (error.response && error.response.data) {
console.error('Infobip Error Details:', JSON.stringify(error.response.data, null, 2));
} else {
console.error('Full Error Object:', error);
}
// Re-throw the error to be handled by the calling function (API route)thrownewError(`Failed to send MMS: ${error.message}`);
}
}
module.exports = {
sendMms,
};
Explanation:
We import the Infobip client and AuthType enum from the SDK.
We check for required environment variables. Throwing an error here will prevent the application from starting if they are missing.
We initialize the infobip client instance using the API Key and Base URL loaded from process.env. We explicitly state we're using AuthType.ApiKey.
The sendMms function takes the necessary parameters (to, from, mediaUrl, caption).
It constructs the mmsPayload object matching the structure potentially expected by the Infobip MMS API (e.g., /mms/1/single or similar, often requiring a messages array). Consult the latest Infobip MMS API documentation for the exact payload fields and structure.
We use a try...catch block to handle potential errors during the API call.
infobip.channels.mms.send(...) is the presumed SDK method to send the message. Note:The exact SDK method name, import path, and payload structure should be verified against the current version of @infobip-api/sdk documentation. SDK structures may change between major versions.
Errors are logged with details, and the error is re-thrown to be caught by the API route handler.
The function is exported for use in our Fastify server.
3. Building the API Layer (Fastify Server and Route)
Now, let's set up the Fastify server and create the API endpoint that will use our infobipService.
Create server.js:
In the project root, create the server.js file.
Implement the Fastify Server:
Add the following code to server.js:
JavaScript
// server.js'use strict';
// Load environment variables from .env filerequire('dotenv').config();
// Check required environment variables before proceedingif (!process.env.INFOBIP_API_KEY || !process.env.INFOBIP_BASE_URL) {
console.error('Startup Error: Missing Infobip API Key or Base URL. Check .env file.');
process.exit(1); // Exit if critical config is missing
}
const fastify = require('fastify')({
logger: {
level: process.env.LOG_LEVEL || 'info', // Use log level from env or default to infotransport: process.env.NODE_ENV !== 'production' ? { // Use pino-pretty only in non-productiontarget: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
} : undefined, // Use default JSON logger in production
},
});
// Import the Infobip service *after* checking environment variablesconst infobipService = require('./services/infobipService');
// --- API Routes ---// Basic health check route
fastify.get('/', async (request, reply) => {
return { status: 'ok', timestamp: newDate().toISOString() };
});
// Define schema for request body validationconst sendMmsSchema = {
body: {
type: 'object',
required: ['to', 'from', 'mediaUrl'],
properties: {
to: { type: 'string', description: 'Recipient phone number (E.164 format)', pattern: '^\\+[1-9]\\d{1,14}$' }, // E.164 patternfrom: { type: 'string', description: 'Sender phone number (E.164 format)', pattern: '^\\+[1-9]\\d{1,14}$' }, // E.164 patternmediaUrl: { type: 'string', format: 'url', description: 'Publicly accessible URL for the media' },
caption: { type: 'string', description: 'Optional caption for the media' },
},
additionalProperties: false// Disallow properties not defined in the schema
},
response: { // Optional: Define expected response schemas200: {
type: 'object',
properties: {
messageId: { type: 'string' },
status: { type: 'string' },
details: { type: 'object' } // Include the full Infobip response
}
},
// Add schemas for error responses (400, 500) if desired
}
};
// MMS Sending Route
fastify.post('/send-mms', { schema: sendMmsSchema }, async (request, reply) => {
const { to, from, mediaUrl, caption } = request.body;
request.log.info(`Received request to send MMS to ${to} from ${from}`);
try {
const result = await infobipService.sendMms(to, from, mediaUrl, caption);
// Extract relevant info for the client response.// **Important:** Adjust this parsing based on the *actual* Infobip API/SDK response structure.// The example below assumes the primary message status is within `result.messages[0]`.const firstMessageResult = result?.messages?.[0]; // Use optional chaining for safetyconst responsePayload = {
messageId: firstMessageResult?.messageId || 'N/A',
status: firstMessageResult?.status?.groupName || 'UNKNOWN',
details: result, // Send back the full Infobip response for context
};
reply.code(200).send(responsePayload);
} catch (error) {
request.log.error(`Error in /send-mms route: ${error.message}`);
// Determine appropriate status code (e.g., 400 for validation issues caught by service, 500 for API/internal errors)// For simplicity here, we send 500 for any error originating from the service layer call.
reply.code(500).send({ error: 'Failed to send MMS', details: error.message });
}
});
// --- Start Server ---conststart = async () => {
try {
const port = process.env.PORT || 3000;
await fastify.listen({ port: parseInt(port, 10), host: '0.0.0.0' }); // Listen on all available network interfaces// fastify.log is available after listen resolves successfully
fastify.log.info(`Server listening on port ${port}`);
} catch (err) {
// Use console.error for startup errors as logger might not be readyconsole.error('Error starting server:', err);
process.exit(1);
}
};
start();
Explanation:
We load environment variables using require('dotenv').config(); at the very top.
A critical check for INFOBIP_API_KEY and INFOBIP_BASE_URL is added before initializing Fastify or requiring the service, causing the app to exit if they are missing.
A Fastify instance is created with logging enabled. We conditionally use pino-pretty for development logging readability (install it with npm install --save-dev pino-pretty). Production uses standard JSON logging.
The infobipService is imported after the environment variable check.
A basic GET / route is added for health checks.
A sendMmsSchema is defined using Fastify's schema validation. This ensures incoming requests have the required fields (to, from, mediaUrl), checks their types and formats (URL, E.164 phone number pattern: `^\\+[1-9]\\d{1,14}$`), and disallows extra properties. This provides input validation and security.
The POST /send-mms route is defined:
It uses the sendMmsSchema for automatic request validation. If validation fails, Fastify automatically sends a 400 Bad Request response.
It extracts the validated data from request.body.
It calls infobipService.sendMms within a try...catch block.
On success, it constructs a response payload (attempting to parse the Infobip response; verify this parsing against actual API responses) and sends a 200 OK status.
On failure (error caught from the service), it logs the error and sends a 500 Internal Server Error response.
The start function listens on the configured port (from .env or default 3000) and handles server startup errors. Note the use of parseInt(port, 10) for the port number.
Add Start Script:
Modify your package.json to include start scripts.
JSON
// package.json{""name"":""fastify-infobip-mms"",""version"":""1.0.0"",""description"":"""",""main"":""server.js"",""scripts"":{""start"":""node server.js"",""dev"":""node --watch server.js | pino-pretty"",""test"":""echo \""Error: no test specified\"" && exit 1""},""keywords"":[],""author"":"""",""license"":""ISC"",""dependencies"":{""@infobip-api/sdk"":""^X.X.X"",// Replace with actual installed version""dotenv"":""^X.X.X"",// Replace with actual installed version""fastify"":""^X.X.X""// Replace with actual installed version},""devDependencies"":{""pino-pretty"":""^X.X.X""// Replace with actual installed version}}
npm start: Runs the server normally (intended for production, uses JSON logging).
npm run dev: Runs the server using Node's watch mode (restarts on file changes) and pipes logs through pino-pretty for better readability during development. Requires npm install --save-dev pino-pretty.
4. Integrating with Third-Party Services (Infobip Configuration Recap)
We've already integrated the core service, but let's recap the configuration points:
Infobip Account: You need an active account.
API Credentials:
INFOBIP_API_KEY: Obtain this from the Infobip Portal (usually under API Keys). It's used for authentication.
INFOBIP_BASE_URL: Your unique API endpoint URL provided by Infobip (e.g., xxxxx.api.infobip.com). This directs the SDK to the correct API server.
Secure Storage: Store these credentials in the .env file locally and use environment variables in your deployment environment. Never commit your .env file or hardcode credentials directly in the source code.
Sender Number (from): The from number used in the API call must be an MMS-enabled number registered in your Infobip account and authorized for sending.
Media URL (mediaUrl): The URL must be publicly accessible without authentication for Infobip's servers to fetch the media file. Ensure the URL points directly to the image file (JPEG, PNG, etc.).
5. Error Handling, Logging, and Retries
Error Handling Strategy:
Startup: Critical configuration errors (missing API keys) prevent the server from starting.
Validation: Fastify's schema validation handles malformed requests (400 Bad Request).
Service Layer: The infobipService catches errors during the API call (e.g., network issues, invalid credentials, Infobip API errors). It logs detailed error information and throws a generic error upwards.
API Route: The POST /send-mms route catches errors from the service layer and returns a standardized error response to the client (e.g., 500 Internal Server Error).
Logging:
Fastify's built-in logger (request.log) is used for request-related logging. console.log/error is used for startup messages/errors.
Log level is configurable via the LOG_LEVEL environment variable. Use info for production, debug or trace for development/troubleshooting.
pino-pretty enhances log readability during development. In production, use structured JSON logging (Fastify's default without pino-pretty) for easier analysis by log aggregation tools.
Key events logged: Incoming request details, success responses from Infobip, errors encountered at service and route levels, Infobip error details.
For transient network errors or specific Infobip rate-limit responses, implementing a retry strategy with exponential backoff can improve reliability.
Libraries like async-retry can simplify this. Wrap the infobipService.sendMms call or the SDK call within a retry function.
Be cautious not to retry non-recoverable errors (e.g., invalid API key, invalid destination number). Check Infobip's error codes to decide which are retryable.
6. Database Schema and Data Layer (Not Applicable)
This specific guide focuses solely on the API interaction and doesn't require a database. If you needed to store message history, track statuses via webhooks, or manage user data, you would introduce a database (e.g., PostgreSQL, MongoDB) and a data access layer (e.g., using an ORM like Prisma or Sequelize).
7. Security Features
Input Validation: Handled by Fastify's schema validation (sendMmsSchema), preventing malformed data and basic injection attempts. The E.164 pattern (`^\\+[1-9]\\d{1,14}$`) enforces a specific format. additionalProperties: false prevents unexpected data.
Secret Management: API keys and sensitive configuration are managed via environment variables (.env locally, platform environment variables in production). .gitignore prevents accidental commits.
Rate Limiting: Protect your API from abuse. Use a plugin like @fastify/rate-limit.
Bash
npm install @fastify/rate-limit
JavaScript
// In server.js, register the plugin *before* your routes// Make sure to register plugins asynchronously if needed, e.g., inside the start function or using fastify-pluginawait fastify.register(require('@fastify/rate-limit'), {
max: 100, // Max requests per window per IPtimeWindow: '1 minute'
});
HTTPS: Ensure your deployed application runs over HTTPS to encrypt communication. This is typically handled by your hosting provider or load balancer (reverse proxy).
Helmet: Set various security-related HTTP headers using @fastify/helmet.
Bash
npm install @fastify/helmet
JavaScript
// In server.js, register the plugin *before* your routesawait fastify.register(require('@fastify/helmet'));
8. Handling Special Cases
Phone Number Formatting: The API expects E.164 format (e.g., +14155552671). The schema validates this format using the regex `^\\+[1-9]\\d{1,14}$`. More complex validation might be needed depending on specific regional requirements or if you need to normalize different input formats.
ITU-T Recommendation E.164 (June 2020):
The E.164 format supports up to 15 digits total, with 1-3 digit country codes. The format is + followed by the country code and the national significant number. The country code is a sequence of 1 to 3 digits, and the national significant number is a sequence of 1 to 14 digits. The regex pattern `^\\+[1-9]\\d{1,14}$` is used to validate this format.
Media URL Accessibility: Reiterate that the mediaUrl must be public and directly accessible. Internal or authenticated URLs will cause Infobip to fail fetching the media, resulting in an error.
Character Limits: Captions might have character limits imposed by carriers or Infobip. Check Infobip documentation for specific limits.
Infobip API Errors: Handle specific error codes returned by Infobip (e.g., insufficient funds, invalid number, blocked destination). The current implementation logs the details; a production system might parse error.response.data from the SDK error to map these to more user-friendly error messages or trigger specific alerts.
9. Performance Optimizations (Considerations)
Fastify: Chosen for its high performance.
SDK Efficiency: The official SDK is generally optimized for API interaction.
Asynchronous Operations: Node.js and Fastify are inherently asynchronous, handling I/O (like network calls to Infobip) efficiently without blocking the main thread.
Caching: Not directly applicable for sending unique messages, but if retrieving message statuses frequently or using templates, caching could be relevant.
Load Testing: Use tools like k6, autocannon, or wrk to test the endpoint's performance under load, especially if high throughput is expected. Identify bottlenecks (CPU, memory, network latency to Infobip).
10. Monitoring, Observability, and Analytics (Considerations)
Health Checks: The GET / route provides a basic health check. More sophisticated checks could verify connectivity to Infobip (e.g., by making a low-impact, authenticated API call like fetching account balance, if available).
Metrics: Integrate with monitoring tools (e.g., Prometheus via fastify-metrics, Datadog). Track request rates, error rates (HTTP 4xx/5xx), latency (overall API response time and specifically the latency of the infobipService.sendMms call).
Error Tracking: Use services like Sentry (@sentry/node) or Datadog APM to capture and aggregate errors in production, providing stack traces and context for easier debugging.
Logging Aggregation: In production, configure Fastify's logger to output JSON and ship these logs to a centralized logging platform (e.g., ELK stack, Splunk, Datadog Logs, Grafana Loki) for analysis, searching, and alerting.
Infobip Reports: Utilize Infobip's dashboard and potentially configure delivery report webhooks (using the notifyUrl parameter in the payload) to track message delivery status and analytics directly from Infobip.
11. Troubleshooting and Caveats
Invalid login details Error (from Infobip):
Cause: Incorrect INFOBIP_API_KEY or INFOBIP_BASE_URL.
Solution: Double-check the values in your .env file (or deployment environment variables) against the Infobip portal. Ensure the Base URL is correct for your account region and API key type.
Failed to fetch media Error (or similar):
Cause: The mediaUrl provided is not publicly accessible, points to a non-media file, is malformed, the hosting server blocked Infobip's request (e.g., firewall, User-Agent block), or the URL timed out.
Solution: Verify the URL works in a browser's incognito mode or using curl. Ensure it links directly to the image. Check your media server logs if you host the media. Ensure the media type is supported by Infobip MMS.
Invalid ""To"" address Error:
Cause: The recipient number (to) is not in valid E.164 format (+ followed by country code and number, e.g., +14155552671), is invalid for the destination country, or is on a blacklist.
Solution: Ensure the number strictly follows the E.164 format. Check Infobip documentation for any specific number validation rules or country restrictions.
Message sending not enabled for number Error:
Cause: The from number is not configured or approved for sending MMS in your Infobip account, or MMS sending is not enabled for the specific destination country/carrier.
Solution: Contact Infobip support or check your number configuration and capabilities in their portal.
Free Trial Limitations: Remember that free trial accounts typically restrict sending to the registered phone number only and may have other limitations.
SDK Version Compatibility: Ensure you are using a compatible version of the @infobip-api/sdk with your Node.js version. Check the SDK's documentation or package.json for requirements. Method calls (like infobip.channels.mms.send) and payload structures might change between major SDK versions. Always refer to the specific SDK version documentation you are using.
Rate Limits: Hitting Infobip's API rate limits will result in errors (often HTTP 429 Too Many Requests). Implement rate limiting on your API (Section 7) and consider implementing retries with exponential backoff for 429 errors in your service layer.
12. Deployment and CI/CD
Environment Configuration: Do not deploy your .env file. Configure INFOBIP_API_KEY, INFOBIP_BASE_URL, PORT, LOG_LEVEL, and NODE_ENV=production using your deployment platform's environment variable management system (e.g., Heroku Config Vars, AWS System Manager Parameter Store, Kubernetes Secrets, Docker environment variables).
Deployment Platforms:
PaaS (e.g., Heroku, Render): Simple deployment via Git push. Configure environment variables through their dashboard/CLI. Ensure your package.json specifies the correct Node.js engine.
Containers (Docker): Create a Dockerfile to package your application. Manage containers using Docker Compose (for simple setups), Kubernetes, or managed services like AWS ECS/Fargate, Google Cloud Run.
Serverless (e.g., AWS Lambda, Google Cloud Functions): Adapt the Fastify app using adapters like aws-lambda-fastify or @fastify/aws-lambda, or potentially restructure as individual functions triggered by API Gateway.
Dockerfile Example:
Dockerfile
# Use an official Node.js runtime as a parent image (Choose LTS version like 18 or 20)
FROM node:18-alpine
# Set the working directory in the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json (or yarn.lock)
# Copy only necessary files first to leverage Docker cache
COPY package.json package-lock.json* ./
# Or: COPY package.json yarn.lock ./
# Install production dependencies
# Using npm ci is generally recommended in CI/CD for reproducible builds
RUN npm ci --only=production
# Or: RUN yarn install --production --frozen-lockfile
# Bundle app source code
COPY . .
# Expose the port the app runs on (should match PORT env var, default 3000)
EXPOSE 3000
# Define the command to run your app in production
# Set NODE_ENV=production explicitly if not done elsewhere
ENV NODE_ENV=production
# Use array form for CMD
CMD [ ""node"", ""server.js"" ]
Setup Node.js: Specify the correct Node.js version.
Install Dependencies:npm ci or yarn install --frozen-lockfile.
Lint/Format Check: Run tools like ESLint/Prettier (npm run lint - requires adding a lint script).
(Optional) Security Scan: Run vulnerability scans (e.g., npm audit, Snyk).
Build Docker Image (if using containers). Tag the image appropriately.
Push Image to Registry (if using containers): e.g., Docker Hub, AWS ECR, Google GAR.
Deploy: Trigger deployment to your hosting platform (e.g., using platform CLI commands, Infrastructure as Code tools like Terraform, or built-in integrations).
Rollback Strategy: Ensure your deployment process allows for easy rollback to a previous stable version in case of deployment failures or runtime issues in the new version.
13. Verification and Testing
Manual Verification:
Ensure your .env file has correct credentials and a valid public mediaUrl.
Start the server: npm run dev (for pretty logs) or npm start.
Use curl or a tool like Postman/Insomnia to send a POST request to http://localhost:3000/send-mms (or your configured port).
curl Example:
Important: Replace +1xxxxxxxxxx with your actual recipient phone number (must be allowed by your Infobip account, e.g., your registered number on a free trial).
Replace +1yyyyyyyyyy with your valid, MMS-enabled Infobip sender number.
Replace the mediaUrl with a working, publicly accessible image URL (e.g., https://placehold.co/600x400.png).
MMS (Multimedia Messaging Service) allows you to send messages that include multimedia content such as images, videos, and audio files, while SMS (Short Message Service) is limited to text only. MMS messages can be up to 1600 characters and support rich media attachments.
Which Node.js versions are compatible with Fastify and Infobip SDK?
This implementation works with Node.js LTS versions v18, v20, and v22. Node.js v14 reached end-of-life on April 30, 2023, and is no longer recommended. Always use an actively maintained LTS version for production deployments.
How do I get Infobip API credentials?
Sign up for an Infobip account, then navigate to your dashboard to find your API Key and Base URL. Free trial accounts have limitations, typically allowing messages only to your registered phone number.
What image formats are supported for MMS messages?
Infobip typically supports JPEG, PNG, and GIF formats for MMS media. Always verify current supported formats in the Infobip MMS API documentation as specifications may change.
Why am I getting "Failed to fetch media" errors?
This error occurs when the mediaUrl is not publicly accessible. Ensure the URL: (1) is publicly accessible without authentication, (2) points directly to an image file, (3) uses HTTPS, and (4) doesn't block Infobip's servers via firewall or User-Agent restrictions.
How do I validate phone numbers in E.164 format?
E.164 format requires a + prefix followed by 1-3 digit country code and the national number, with a maximum of 15 digits total. The regex pattern ^\\+[1-9]\\d{1,14}$ validates this format as specified in ITU-T Recommendation E.164 (June 2020).
Can I send MMS to any phone number with Infobip?
With free trial accounts, you can typically only send messages to the phone number used during registration. Production accounts can send to any valid phone number, subject to regional regulations and carrier support for MMS.
How do I implement retry logic for failed MMS sends?
Use libraries like async-retry to wrap the infobipService.sendMms call with exponential backoff. Only retry transient errors (network issues, rate limits) and avoid retrying non-recoverable errors (invalid credentials, blocked destinations).
Summary
You now have a complete, production-ready implementation for sending MMS messages using Fastify and the Infobip API. This guide covered project setup, Infobip SDK integration, request validation, error handling, security best practices, and deployment strategies. The implementation provides a solid foundation that you can extend with features like delivery tracking, message queuing, or integration with other communication channels.
Frequently asked questions
Create a Fastify server with a POST /send-mms route, use the Infobip SDK to format the MMS message, and send it via the Infobip API. The request body should contain recipient, sender, media URL, and an optional caption. Ensure your Infobip account and sender number are MMS-enabled.
The Infobip Node.js SDK (@infobip-api/sdk) simplifies interaction with the Infobip API, offering convenient methods for sending SMS, MMS, and other communication types. It handles authentication, request formatting, and response parsing, making integration smoother.
Fastify is a high-performance Node.js web framework known for its speed and extensibility. Its schema validation feature helps ensure data integrity and security when handling incoming MMS requests. It's an excellent choice for building robust and efficient API endpoints.
Check your Infobip API key and base URL during server startup. This prevents the application from launching if critical credentials are missing. In server.js, verify these environment variables before initializing Fastify or requiring any services.
Dotenv loads environment variables from a .env file into process.env during development. This keeps sensitive API credentials out of your codebase and allows easy configuration changes without modifying core application files. Never commit .env to version control.
Implement a try-catch block in your service function and route handler. Catch errors originating from the Infobip API call, log detailed error messages including status codes, and return a user-friendly error response to the client. Check Infobip error codes for specific issues.
The Infobip MMS API requires a specific JSON structure for sending MMS messages, typically including an array called "messages", even for single MMS sends. Each message object needs recipient ('to'), sender ('from'), content object with media URL ('mediaUrl'), and an optional caption ('caption').
Phone numbers must be in E.164 format, e.g., +14155552671 (+ followed by country code and number). Validate phone numbers using the E.164 pattern (^+[1-9]\d{1,14}$) during schema validation in your API endpoint. This prevents sending requests with invalid numbers.
No, the media URL for MMS messages must be publicly accessible without authentication. Infobip servers must be able to fetch the media directly from the URL. Internal network or private URLs will cause errors.
Create a directory for your project, initialize npm, install Fastify, the Infobip SDK, and dotenv. Create separate files for server logic (server.js), service functions (e.g., services/infobipService.js), and environment variables (.env, .env.example, .gitignore).
Use a plugin like fastify-rate-limit. Register the plugin in your server.js before your API routes. Configure limits like max requests per IP within a time window to prevent abuse and maintain a healthy system load.
Deploy your application to PaaS services like Heroku or Render via Git. For containerized deployments, build a Docker image using a Dockerfile. For serverless, adapt your app for platforms like AWS Lambda or Google Cloud Functions.
Input validation, through Fastify's JSON schema, prevents malformed data, potential injection attacks, and improves the robustness of your application. Using Fastify's schema, you can enforce required fields and specific data types, ensuring the validity of incoming requests.
Use environment variables for API keys and secrets. Implement input validation with Fastify's schema validation. Use rate limiting (e.g., fastify-rate-limit) to prevent abuse. Set secure HTTP headers with fastify-helmet. Run the application over HTTPS in production.
This comprehensive guide shows you how to build a production-ready Node.js application using the Fastify framework to send Multimedia Messaging Service (MMS) messages via the Infobip API. Whether you're adding multimedia messaging capabilities to an existing application or building a new messaging service, this tutorial covers everything from initial project setup and API configuration to error handling, security best practices, and deployment strategies.
By the end of this tutorial, you'll have a fully functional Fastify API endpoint capable of accepting MMS details (recipient, sender, media URL, caption) and relaying them through Infobip's messaging infrastructure for reliable delivery. This implementation solves the common need to programmatically integrate rich media messaging into applications without manual intervention.
Project Overview and Goals
@infobip-api/sdk: The official Infobip Node.js SDK, simplifying interaction with their API.dotenv: For managing environment variables securely.POST /send-mmsendpoint that takes JSON payload and sends an MMS message using credentials stored in environment variables.System Architecture:
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 fastify-infobip-mms cd fastify-infobip-mmsInitialize Node.js Project: Initialize the project using npm. The
-yflag accepts the default settings.This creates a
package.jsonfile.Install Dependencies: We need Fastify for the web server, the Infobip SDK to interact with their API, and
dotenvto manage environment variables.Create Project Structure: Set up a basic directory structure for organization:
Configure
.gitignore: Create a.gitignorefile in the project root and addnode_modulesand.envto prevent committing them to version control.Set up Environment Variables: Create two files in the root directory:
.env(for your actual secrets) and.env.example(as a template)..env.example:.env: Copy.env.exampleto.envand replace the placeholder values with your actual Infobip API Key and Base URL obtained from your Infobip dashboard. Set the desired port.dotenvloads these variables intoprocess.envfor easy access in your Node.js application during development.2. Implementing Core Functionality (Infobip Service)
Now, let's create the service module responsible for handling communication with the Infobip API using their SDK.
Create
infobipService.js: Inside theservicesdirectory, create theinfobipService.jsfile.Implement the Service: Add the following code to
services/infobipService.js.// services/infobipService.js 'use strict'; const { Infobip, AuthType } = require('@infobip-api/sdk'); // Check for essential environment variables during module load. // In a production application, this check is often better placed in the main // application entry point (server.js) before server initialization, // allowing for a more controlled shutdown or error reporting. if (!process.env.INFOBIP_API_KEY || !process.env.INFOBIP_BASE_URL) { console.error('Error: Missing Infobip API Key or Base URL in environment variables.'); // Throwing an error here will typically stop the application startup process. throw new Error('Missing Infobip API Key or Base URL in environment variables.'); } // Initialize the Infobip client const infobip = new Infobip({ baseUrl: process.env.INFOBIP_BASE_URL, apiKey: process.env.INFOBIP_API_KEY, authType: AuthType.ApiKey, // Specify API Key authentication }); /** * Sends an MMS message using the Infobip API. * @param {string} to - The recipient's phone number in E.164 format (e.g., +14155552671). * @param {string} from - Your Infobip MMS-enabled sender number in E.164 format. * @param {string} mediaUrl - The publicly accessible URL of the media file (image). * @param {string} [caption] - Optional caption text for the media. * @returns {Promise<object>} - The response object from the Infobip API. * @throws {Error} - Throws an error if the API call fails. */ async function sendMms(to, from, mediaUrl, caption) { console.log(`Attempting to send MMS: to=${to}, from=${from}, mediaUrl=${mediaUrl}`); // Basic logging // Construct the MMS payload according to Infobip SDK/API requirements const mmsPayload = { from: from, to: to, content: { mediaUrl: mediaUrl, // caption: caption || '', // Include caption if provided // mediaType: 'IMAGE' // You might need to specify this depending on SDK version/API behavior }, // Optional: Add other parameters like delivery reports callback URL etc. // notifyUrl: 'YOUR_DELIVERY_REPORT_WEBHOOK_URL' }; // Add caption only if it exists if (caption) { mmsPayload.content.caption = caption; } try { // Use the Infobip SDK to send the MMS message via the 'mms' channel // The specific method might vary slightly based on SDK updates, check SDK docs. // Assuming a method like `infobip.channels.mms.send(payload)` exists. // Referencing potential structure based on SDK patterns. The MMS API // might expect messages in an array even for single sends. Verify this structure. const response = await infobip.channels.mms.send({ messages: [mmsPayload] }); console.log('Infobip API Response:', JSON.stringify(response, null, 2)); return response; // Return the successful response body } catch (error) { console.error('Error sending MMS via Infobip:', error.message); // Log the full error if available (might contain more details from Infobip) if (error.response && error.response.data) { console.error('Infobip Error Details:', JSON.stringify(error.response.data, null, 2)); } else { console.error('Full Error Object:', error); } // Re-throw the error to be handled by the calling function (API route) throw new Error(`Failed to send MMS: ${error.message}`); } } module.exports = { sendMms, };Infobipclient andAuthTypeenum from the SDK.infobipclient instance using the API Key and Base URL loaded fromprocess.env. We explicitly state we're usingAuthType.ApiKey.sendMmsfunction takes the necessary parameters (to,from,mediaUrl,caption).mmsPayloadobject matching the structure potentially expected by the Infobip MMS API (e.g.,/mms/1/singleor similar, often requiring amessagesarray). Consult the latest Infobip MMS API documentation for the exact payload fields and structure.try...catchblock to handle potential errors during the API call.infobip.channels.mms.send(...)is the presumed SDK method to send the message. Note: The exact SDK method name, import path, and payload structure should be verified against the current version of@infobip-api/sdkdocumentation. SDK structures may change between major versions.3. Building the API Layer (Fastify Server and Route)
Now, let's set up the Fastify server and create the API endpoint that will use our
infobipService.Create
server.js: In the project root, create theserver.jsfile.Implement the Fastify Server: Add the following code to
server.js:// server.js 'use strict'; // Load environment variables from .env file require('dotenv').config(); // Check required environment variables before proceeding if (!process.env.INFOBIP_API_KEY || !process.env.INFOBIP_BASE_URL) { console.error('Startup Error: Missing Infobip API Key or Base URL. Check .env file.'); process.exit(1); // Exit if critical config is missing } const fastify = require('fastify')({ logger: { level: process.env.LOG_LEVEL || 'info', // Use log level from env or default to info transport: process.env.NODE_ENV !== 'production' ? { // Use pino-pretty only in non-production target: 'pino-pretty', options: { translateTime: 'HH:MM:ss Z', ignore: 'pid,hostname', }, } : undefined, // Use default JSON logger in production }, }); // Import the Infobip service *after* checking environment variables const infobipService = require('./services/infobipService'); // --- API Routes --- // Basic health check route fastify.get('/', async (request, reply) => { return { status: 'ok', timestamp: new Date().toISOString() }; }); // Define schema for request body validation const sendMmsSchema = { body: { type: 'object', required: ['to', 'from', 'mediaUrl'], properties: { to: { type: 'string', description: 'Recipient phone number (E.164 format)', pattern: '^\\+[1-9]\\d{1,14}$' }, // E.164 pattern from: { type: 'string', description: 'Sender phone number (E.164 format)', pattern: '^\\+[1-9]\\d{1,14}$' }, // E.164 pattern mediaUrl: { type: 'string', format: 'url', description: 'Publicly accessible URL for the media' }, caption: { type: 'string', description: 'Optional caption for the media' }, }, additionalProperties: false // Disallow properties not defined in the schema }, response: { // Optional: Define expected response schemas 200: { type: 'object', properties: { messageId: { type: 'string' }, status: { type: 'string' }, details: { type: 'object' } // Include the full Infobip response } }, // Add schemas for error responses (400, 500) if desired } }; // MMS Sending Route fastify.post('/send-mms', { schema: sendMmsSchema }, async (request, reply) => { const { to, from, mediaUrl, caption } = request.body; request.log.info(`Received request to send MMS to ${to} from ${from}`); try { const result = await infobipService.sendMms(to, from, mediaUrl, caption); // Extract relevant info for the client response. // **Important:** Adjust this parsing based on the *actual* Infobip API/SDK response structure. // The example below assumes the primary message status is within `result.messages[0]`. const firstMessageResult = result?.messages?.[0]; // Use optional chaining for safety const responsePayload = { messageId: firstMessageResult?.messageId || 'N/A', status: firstMessageResult?.status?.groupName || 'UNKNOWN', details: result, // Send back the full Infobip response for context }; reply.code(200).send(responsePayload); } catch (error) { request.log.error(`Error in /send-mms route: ${error.message}`); // Determine appropriate status code (e.g., 400 for validation issues caught by service, 500 for API/internal errors) // For simplicity here, we send 500 for any error originating from the service layer call. reply.code(500).send({ error: 'Failed to send MMS', details: error.message }); } }); // --- Start Server --- const start = async () => { try { const port = process.env.PORT || 3000; await fastify.listen({ port: parseInt(port, 10), host: '0.0.0.0' }); // Listen on all available network interfaces // fastify.log is available after listen resolves successfully fastify.log.info(`Server listening on port ${port}`); } catch (err) { // Use console.error for startup errors as logger might not be ready console.error('Error starting server:', err); process.exit(1); } }; start();require('dotenv').config();at the very top.INFOBIP_API_KEYandINFOBIP_BASE_URLis added before initializing Fastify or requiring the service, causing the app to exit if they are missing.pino-prettyfor development logging readability (install it withnpm install --save-dev pino-pretty). Production uses standard JSON logging.infobipServiceis imported after the environment variable check.GET /route is added for health checks.sendMmsSchemais defined using Fastify's schema validation. This ensures incoming requests have the required fields (to,from,mediaUrl), checks their types and formats (URL, E.164 phone number pattern:`^\\+[1-9]\\d{1,14}$`), and disallows extra properties. This provides input validation and security.POST /send-mmsroute is defined:sendMmsSchemafor automatic request validation. If validation fails, Fastify automatically sends a 400 Bad Request response.request.body.infobipService.sendMmswithin atry...catchblock.startfunction listens on the configured port (from.envor default 3000) and handles server startup errors. Note the use ofparseInt(port, 10)for the port number.Add Start Script: Modify your
package.jsonto include start scripts.// package.json { ""name"": ""fastify-infobip-mms"", ""version"": ""1.0.0"", ""description"": """", ""main"": ""server.js"", ""scripts"": { ""start"": ""node server.js"", ""dev"": ""node --watch server.js | pino-pretty"", ""test"": ""echo \""Error: no test specified\"" && exit 1"" }, ""keywords"": [], ""author"": """", ""license"": ""ISC"", ""dependencies"": { ""@infobip-api/sdk"": ""^X.X.X"", // Replace with actual installed version ""dotenv"": ""^X.X.X"", // Replace with actual installed version ""fastify"": ""^X.X.X"" // Replace with actual installed version }, ""devDependencies"": { ""pino-pretty"": ""^X.X.X"" // Replace with actual installed version } }npm start: Runs the server normally (intended for production, uses JSON logging).npm run dev: Runs the server using Node's watch mode (restarts on file changes) and pipes logs throughpino-prettyfor better readability during development. Requiresnpm install --save-dev pino-pretty.4. Integrating with Third-Party Services (Infobip Configuration Recap)
We've already integrated the core service, but let's recap the configuration points:
INFOBIP_API_KEY: Obtain this from the Infobip Portal (usually under API Keys). It's used for authentication.INFOBIP_BASE_URL: Your unique API endpoint URL provided by Infobip (e.g.,xxxxx.api.infobip.com). This directs the SDK to the correct API server..envfile locally and use environment variables in your deployment environment. Never commit your.envfile or hardcode credentials directly in the source code.from): Thefromnumber used in the API call must be an MMS-enabled number registered in your Infobip account and authorized for sending.mediaUrl): The URL must be publicly accessible without authentication for Infobip's servers to fetch the media file. Ensure the URL points directly to the image file (JPEG, PNG, etc.).5. Error Handling, Logging, and Retries
infobipServicecatches errors during the API call (e.g., network issues, invalid credentials, Infobip API errors). It logs detailed error information and throws a generic error upwards.POST /send-mmsroute catches errors from the service layer and returns a standardized error response to the client (e.g., 500 Internal Server Error).request.log) is used for request-related logging.console.log/erroris used for startup messages/errors.LOG_LEVELenvironment variable. Useinfofor production,debugortracefor development/troubleshooting.pino-prettyenhances log readability during development. In production, use structured JSON logging (Fastify's default withoutpino-pretty) for easier analysis by log aggregation tools.async-retrycan simplify this. Wrap theinfobipService.sendMmscall or the SDK call within a retry function.6. Database Schema and Data Layer (Not Applicable)
This specific guide focuses solely on the API interaction and doesn't require a database. If you needed to store message history, track statuses via webhooks, or manage user data, you would introduce a database (e.g., PostgreSQL, MongoDB) and a data access layer (e.g., using an ORM like Prisma or Sequelize).
7. Security Features
Input Validation: Handled by Fastify's schema validation (
sendMmsSchema), preventing malformed data and basic injection attempts. The E.164 pattern (`^\\+[1-9]\\d{1,14}$`) enforces a specific format.additionalProperties: falseprevents unexpected data.Secret Management: API keys and sensitive configuration are managed via environment variables (
.envlocally, platform environment variables in production)..gitignoreprevents accidental commits.Rate Limiting: Protect your API from abuse. Use a plugin like
@fastify/rate-limit.// In server.js, register the plugin *before* your routes // Make sure to register plugins asynchronously if needed, e.g., inside the start function or using fastify-plugin await fastify.register(require('@fastify/rate-limit'), { max: 100, // Max requests per window per IP timeWindow: '1 minute' });HTTPS: Ensure your deployed application runs over HTTPS to encrypt communication. This is typically handled by your hosting provider or load balancer (reverse proxy).
Helmet: Set various security-related HTTP headers using
@fastify/helmet.// In server.js, register the plugin *before* your routes await fastify.register(require('@fastify/helmet'));8. Handling Special Cases
Phone Number Formatting: The API expects E.164 format (e.g.,
+14155552671). The schema validates this format using the regex`^\\+[1-9]\\d{1,14}$`. More complex validation might be needed depending on specific regional requirements or if you need to normalize different input formats.ITU-T Recommendation E.164 (June 2020): The E.164 format supports up to 15 digits total, with 1-3 digit country codes. The format is
+followed by the country code and the national significant number. The country code is a sequence of 1 to 3 digits, and the national significant number is a sequence of 1 to 14 digits. The regex pattern`^\\+[1-9]\\d{1,14}$`is used to validate this format.Media URL Accessibility: Reiterate that the
mediaUrlmust be public and directly accessible. Internal or authenticated URLs will cause Infobip to fail fetching the media, resulting in an error.Character Limits: Captions might have character limits imposed by carriers or Infobip. Check Infobip documentation for specific limits.
Infobip API Errors: Handle specific error codes returned by Infobip (e.g., insufficient funds, invalid number, blocked destination). The current implementation logs the details; a production system might parse
error.response.datafrom the SDK error to map these to more user-friendly error messages or trigger specific alerts.9. Performance Optimizations (Considerations)
k6,autocannon, orwrkto test the endpoint's performance under load, especially if high throughput is expected. Identify bottlenecks (CPU, memory, network latency to Infobip).10. Monitoring, Observability, and Analytics (Considerations)
GET /route provides a basic health check. More sophisticated checks could verify connectivity to Infobip (e.g., by making a low-impact, authenticated API call like fetching account balance, if available).fastify-metrics, Datadog). Track request rates, error rates (HTTP 4xx/5xx), latency (overall API response time and specifically the latency of theinfobipService.sendMmscall).@sentry/node) or Datadog APM to capture and aggregate errors in production, providing stack traces and context for easier debugging.notifyUrlparameter in the payload) to track message delivery status and analytics directly from Infobip.11. Troubleshooting and Caveats
Invalid login detailsError (from Infobip):INFOBIP_API_KEYorINFOBIP_BASE_URL..envfile (or deployment environment variables) against the Infobip portal. Ensure the Base URL is correct for your account region and API key type.Failed to fetch mediaError (or similar):mediaUrlprovided is not publicly accessible, points to a non-media file, is malformed, the hosting server blocked Infobip's request (e.g., firewall, User-Agent block), or the URL timed out.curl. Ensure it links directly to the image. Check your media server logs if you host the media. Ensure the media type is supported by Infobip MMS.Invalid ""To"" addressError:to) is not in valid E.164 format (+followed by country code and number, e.g.,+14155552671), is invalid for the destination country, or is on a blacklist.Message sending not enabled for numberError:fromnumber is not configured or approved for sending MMS in your Infobip account, or MMS sending is not enabled for the specific destination country/carrier.@infobip-api/sdkwith your Node.js version. Check the SDK's documentation orpackage.jsonfor requirements. Method calls (likeinfobip.channels.mms.send) and payload structures might change between major SDK versions. Always refer to the specific SDK version documentation you are using.12. Deployment and CI/CD
Environment Configuration: Do not deploy your
.envfile. ConfigureINFOBIP_API_KEY,INFOBIP_BASE_URL,PORT,LOG_LEVEL, andNODE_ENV=productionusing your deployment platform's environment variable management system (e.g., Heroku Config Vars, AWS System Manager Parameter Store, Kubernetes Secrets, Docker environment variables).Deployment Platforms:
package.jsonspecifies the correct Node.js engine.Dockerfileto package your application. Manage containers using Docker Compose (for simple setups), Kubernetes, or managed services like AWS ECS/Fargate, Google Cloud Run.aws-lambda-fastifyor@fastify/aws-lambda, or potentially restructure as individual functions triggered by API Gateway.DockerfileExample:CI/CD Pipeline (e.g., GitHub Actions, GitLab CI, Jenkins):
npm cioryarn install --frozen-lockfile.npm run lint- requires adding a lint script).npm audit, Snyk).Rollback Strategy: Ensure your deployment process allows for easy rollback to a previous stable version in case of deployment failures or runtime issues in the new version.
13. Verification and Testing
Ensure your
.envfile has correct credentials and a valid publicmediaUrl.Start the server:
npm run dev(for pretty logs) ornpm start.Use
curlor a tool like Postman/Insomnia to send a POST request tohttp://localhost:3000/send-mms(or your configured port).curlExample:+1xxxxxxxxxxwith your actual recipient phone number (must be allowed by your Infobip account, e.g., your registered number on a free trial).+1yyyyyyyyyywith your valid, MMS-enabled Infobip sender number.mediaUrlwith a working, publicly accessible image URL (e.g.,https://placehold.co/600x400.png).curl -X POST http://localhost:3000/send-mms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+1xxxxxxxxxx"", ""from"": ""+1yyyyyyyyyy"", ""mediaUrl"": ""https://placehold.co/600x400.png"", ""caption"": ""Hello from Fastify!"" }'Frequently Asked Questions (FAQ)
What is MMS and how does it differ from SMS?
MMS (Multimedia Messaging Service) allows you to send messages that include multimedia content such as images, videos, and audio files, while SMS (Short Message Service) is limited to text only. MMS messages can be up to 1600 characters and support rich media attachments.
Which Node.js versions are compatible with Fastify and Infobip SDK?
This implementation works with Node.js LTS versions v18, v20, and v22. Node.js v14 reached end-of-life on April 30, 2023, and is no longer recommended. Always use an actively maintained LTS version for production deployments.
How do I get Infobip API credentials?
Sign up for an Infobip account, then navigate to your dashboard to find your API Key and Base URL. Free trial accounts have limitations, typically allowing messages only to your registered phone number.
What image formats are supported for MMS messages?
Infobip typically supports JPEG, PNG, and GIF formats for MMS media. Always verify current supported formats in the Infobip MMS API documentation as specifications may change.
Why am I getting "Failed to fetch media" errors?
This error occurs when the
mediaUrlis not publicly accessible. Ensure the URL: (1) is publicly accessible without authentication, (2) points directly to an image file, (3) uses HTTPS, and (4) doesn't block Infobip's servers via firewall or User-Agent restrictions.How do I validate phone numbers in E.164 format?
E.164 format requires a
+prefix followed by 1-3 digit country code and the national number, with a maximum of 15 digits total. The regex pattern^\\+[1-9]\\d{1,14}$validates this format as specified in ITU-T Recommendation E.164 (June 2020).Can I send MMS to any phone number with Infobip?
With free trial accounts, you can typically only send messages to the phone number used during registration. Production accounts can send to any valid phone number, subject to regional regulations and carrier support for MMS.
How do I implement retry logic for failed MMS sends?
Use libraries like
async-retryto wrap theinfobipService.sendMmscall with exponential backoff. Only retry transient errors (network issues, rate limits) and avoid retrying non-recoverable errors (invalid credentials, blocked destinations).Summary
You now have a complete, production-ready implementation for sending MMS messages using Fastify and the Infobip API. This guide covered project setup, Infobip SDK integration, request validation, error handling, security best practices, and deployment strategies. The implementation provides a solid foundation that you can extend with features like delivery tracking, message queuing, or integration with other communication channels.