Send Infobip MMS with Fastify and Node.js: A Developer Guide
This guide provides a complete walkthrough for building a Node.js application using the Fastify framework to send Multimedia Messaging Service (MMS) messages via the Infobip API. We'll cover everything from project setup and configuration to core implementation, error handling, and deployment considerations.
By the end of this tutorial, you will have a functional Fastify API endpoint capable of accepting MMS details (recipient, sender, media URL, caption) and relaying them through Infobip for delivery. This solves the common need to integrate rich media messaging into applications programmatically.
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.
- 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 (v14 or later recommended) and npm (or yarn) installed.
- 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.
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.
mkdir fastify-infobip-mms cd fastify-infobip-mms
-
Initialize Node.js Project: Initialize the project using npm. The
-y
flag accepts the default settings.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.npm install fastify @infobip-api/sdk dotenv
-
Create Project Structure: Set up a basic directory structure for organization:
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 addnode_modules
and.env
to prevent committing them to version control.# .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
:# .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.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.# .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.
dotenv
loads these variables intoprocess.env
for 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 theservices
directory_ create theinfobipService.js
file. -
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, };
- Explanation:
- We import the
Infobip
client andAuthType
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 fromprocess.env
. We explicitly state we're usingAuthType.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 amessages
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: Verify the exact method name and required payload structure in the current@infobip-api/sdk
documentation.- 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.js
file. -
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();
- Explanation:
- We load environment variables using
require('dotenv').config();
at the very top. - A critical check for
INFOBIP_API_KEY
andINFOBIP_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 withnpm 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 atry...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.
- It uses the
- The
start
function listens on the configured port (from.env
or 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.json
to 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-pretty
for 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
.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
): Thefrom
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. Useinfo
for production,debug
ortrace
for development/troubleshooting. pino-pretty
enhances 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-retry
can simplify this. Wrap theinfobipService.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
.npm install @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
.npm install @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. - 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
, orwrk
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 theinfobipService.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
orINFOBIP_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.
- Cause: Incorrect
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.
- Cause: The
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.
- Cause: The recipient number (
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.
- 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/sdk
with your Node.js version. Check the SDK's documentation orpackage.json
for 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
.env
file. ConfigureINFOBIP_API_KEY
,INFOBIP_BASE_URL
,PORT
,LOG_LEVEL
, andNODE_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.
- PaaS (e.g., Heroku, Render): Simple deployment via Git push. Configure environment variables through their dashboard/CLI. Ensure your
-
Dockerfile
Example:# 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 ci
oryarn install --frozen-lockfile
. - Lint/Format Check: Run tools like ESLint/Prettier (
npm run lint
- requires adding a lint script). - Tests: Run unit/integration tests (
npm test
). Ensure tests cover validation, service logic (mocked), and route handling. - Build (if necessary): e.g., TypeScript compilation.
- (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 publicmediaUrl
. -
Start the server:
npm run dev
(for pretty logs) ornpm start
. -
Use
curl
or a tool like Postman/Insomnia to send a POST request tohttp://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
).
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!"" }'
- Important: Replace
-