code examples
code examples
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.
Send MMS with Fastify and Node.js Using Infobip API: Complete Guide
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-mmsendpoint 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.
- An active Infobip account. You can create a free trial account.
- 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:
+-------------+ +-----------------+ +-------------------+ +-------------+
| |--(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.
bashmkdir fastify-infobip-mms cd fastify-infobip-mms -
Initialize Node.js Project: Initialize the project using npm. The
-yflag accepts the default settings.bashnpm init -yThis 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.bashnpm install fastify @infobip-api/sdk dotenv -
Create Project Structure: Set up a basic directory structure for organization:
textfastify-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.gitignorefile in the project root and addnode_modulesand.envto 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.example:dotenv# .env.example # Infobip Credentials INFOBIP_API_KEY=YOUR_INFOBIP_API_KEY_HERE INFOBIP_BASE_URL=YOUR_INFOBIP_BASE_URL_HERE # Application Settings PORT=3000 LOG_LEVEL=info -
.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.dotenv# .env # Infobip Credentials INFOBIP_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx INFOBIP_BASE_URL=xxxxx.api.infobip.com # Application Settings PORT=3000 LOG_LEVEL=info- Purpose: Using environment variables keeps sensitive credentials out of your codebase and enables different configurations for development, staging, and production environments.
dotenvloads these variables intoprocess.envfor easy access in your Node.js application during development.
- Purpose: Using environment variables keeps sensitive credentials out of your codebase and enables different configurations for development, staging, and production environments.
-
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.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. 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, };- Explanation:
- We import the
Infobipclient andAuthTypeenum 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
infobipclient instance using the API Key and Base URL loaded fromprocess.env. We explicitly state we're usingAuthType.ApiKey. - The
sendMmsfunction takes the necessary parameters (to,from,mediaUrl,caption). - It constructs the
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. - We use a
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.- 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.
- We import the
- Explanation:
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:javascript// 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();- Explanation:
- We load environment variables using
require('dotenv').config();at the very top. - A critical check for
INFOBIP_API_KEYandINFOBIP_BASE_URLis 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-prettyfor development logging readability (install it withnpm install --save-dev pino-pretty). Production uses standard JSON logging. - The
infobipServiceis imported after the environment variable check. - A basic
GET /route is added for health checks. - A
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. - The
POST /send-mmsroute is defined:- It uses the
sendMmsSchemafor 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.sendMmswithin atry...catchblock. - 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.
- It uses the
- The
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.
- We load environment variables using
- Explanation:
-
Add Start Script: Modify your
package.jsonto 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 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 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
.envfile locally and use environment variables in your deployment environment. Never commit your.envfile or hardcode credentials directly in the source code. - Sender Number (
from): Thefromnumber 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
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. - API Route: The
POST /send-mmsroute 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/erroris used for startup messages/errors. - Log level is configurable via the
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.- Key events logged: Incoming request details, success responses from Infobip, errors encountered at service and route levels, Infobip error details.
- Fastify's built-in logger (
- Retry Mechanisms (Not Implemented, Considerations):
- For transient network errors or specific Infobip rate-limit responses, implementing a retry strategy with exponential backoff can improve reliability.
- Libraries like
async-retrycan simplify this. Wrap theinfobipService.sendMmscall 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: 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.bashnpm install @fastify/rate-limitjavascript// 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.bashnpm install @fastify/helmetjavascript// 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)
- 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, 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)
- 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 theinfobipService.sendMmscall). - 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
notifyUrlparameter in the payload) to track message delivery status and analytics directly from Infobip.
11. Troubleshooting and Caveats
Invalid login detailsError (from Infobip):- Cause: Incorrect
INFOBIP_API_KEYorINFOBIP_BASE_URL. - Solution: Double-check the values in your
.envfile (or deployment environment variables) against the Infobip portal. Ensure the Base URL is correct for your account region and API key type.
- Cause: Incorrect
Failed to fetch mediaError (or similar):- Cause: The
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. - 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.
- Cause: The
Invalid ""To"" addressError:- 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.
- Cause: The recipient number (
Message sending not enabled for numberError:- Cause: The
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. - Solution: Contact Infobip support or check your number configuration and capabilities in their portal.
- Cause: The
- 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/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. - 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
.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:
- PaaS (e.g., Heroku, Render): Simple deployment via Git push. Configure environment variables through their dashboard/CLI. Ensure your
package.jsonspecifies the correct Node.js engine. - Containers (Docker): Create a
Dockerfileto 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-fastifyor@fastify/aws-lambda, or potentially restructure as individual functions triggered by API Gateway.
- PaaS (e.g., Heroku, Render): Simple deployment via Git push. Configure environment variables through their dashboard/CLI. Ensure your
-
DockerfileExample: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"" ] -
CI/CD Pipeline (e.g., GitHub Actions, GitLab CI, Jenkins):
- Checkout Code: Get the source code.
- Setup Node.js: Specify the correct Node.js version.
- Install Dependencies:
npm cioryarn 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
.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:- Important: Replace
+1xxxxxxxxxxwith your actual recipient phone number (must be allowed by your Infobip account, e.g., your registered number on a free trial). - Replace
+1yyyyyyyyyywith your valid, MMS-enabled Infobip sender number. - Replace the
mediaUrlwith a working, publicly accessible image URL (e.g.,https://placehold.co/600x400.png).
bashcurl -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!"" }' - Important: Replace
-
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 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
How to send MMS with Infobip and Fastify?
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.
What is the Infobip Node.js SDK?
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.
Why use Fastify for sending MMS messages?
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.
When should I check Infobip API credentials?
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.
What is the role of dotenv in this setup?
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.
How to handle Infobip API errors?
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.
What data format does Infobip API expect for MMS?
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').
How to format phone numbers for Infobip?
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.
Can I use a local image URL for Infobip MMS?
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.
How to structure a Node.js project with Fastify?
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).
How to add rate limiting for sending Infobip MMS?
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.
How to deploy the Fastify Infobip MMS app?
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.
Why is input validation important with Fastify?
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.
What are best practices for security in a Node.js and Fastify app?
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.