Frequently Asked Questions
Build a Fastify API endpoint that accepts recipient numbers and message text, then integrates with the Infobip SMS API via its Node.js SDK. This allows your application to send high-volume SMS notifications, alerts, or marketing messages programmatically and reliably. The Fastify framework's performance and plugin architecture make it ideal for this purpose.
The Infobip Node.js SDK simplifies interaction with the Infobip SMS API, allowing you to send SMS messages programmatically within your Node.js application. It handles authentication, request formatting, and response parsing, making integration straightforward.
Fastify is chosen for its speed and extensible plugin system, which are beneficial for high-throughput applications like bulk SMS sending. Its lightweight nature and ease of use also contribute to efficient development and a smaller footprint.
For high-throughput SMS sending, a message queue like BullMQ or RabbitMQ is highly recommended. It decouples API requests from actual SMS delivery, allowing the API to respond quickly while background workers handle sending, retries, and rate limiting.
Yes, storing your Infobip `API_KEY` and `BASE_URL` in environment variables (`.env` file locally, platform secrets in production) is crucial for security. This keeps sensitive information out of your codebase and allows for environment-specific configurations.
Implement a `try...catch` block around the `fastify.infobip.channels.sms.send` call to handle potential errors during the API request. Log error details using `fastify.log.error` and forward relevant error information to the client, including Infobip's error codes if available.
The Infobip plugin initializes and encapsulates the Infobip Node.js SDK client, making it accessible throughout the Fastify application. It uses environment variables to configure the client and provides a reusable way to interact with the Infobip API.
Use the `fastify-rate-limit` plugin to control the rate of incoming requests to your SMS API. This protects against abuse, helps manage costs, and prevents exceeding Infobip's rate limits. Configure the `max` requests and `timeWindow` according to your needs.
Always format phone numbers using the international E.164 format (e.g., +14155552671). While Infobip performs validation, ensuring correct formatting on your end minimizes errors like EC_INVALID_DESTINATION_ADDRESS.
Check Infobip's delivery reports (DLRs) either via webhooks (using the notifyUrl parameter) or by polling the DLR endpoint. These reports provide detailed status information, such as DELIVERED, FAILED, or error codes like EC_ABSENT_SUBSCRIBER, which help identify the reason for non-delivery.
Use environment variables, input validation, rate limiting, HTTPS, and authentication mechanisms like API keys or JWT. Consider using Helmet for setting security headers to protect against common web vulnerabilities.
SMS messages are limited by character encoding: GSM-7 allows 160 characters per segment, while UCS-2 (for non-GSM characters) allows 70. Longer messages are split into multiple segments and cost more. Infobip handles this segmentation automatically.
The 202 Accepted status indicates that the SMS broadcast request has been received and accepted for processing by Infobip. Actual delivery is asynchronous and happens later, the status of which can be tracked via delivery reports.
Use PaaS (Heroku, Render, Fly.io), containers (Docker, Kubernetes), or VMs (EC2, GCE). Ensure environment variables are set correctly in production and use process management tools like PM2. Implement a CI/CD pipeline for automated builds, tests, and deployments.
Use manual verification with curl for basic checks. Implement automated unit and integration tests using a testing framework like Tap or Jest. Mock the Infobip API calls in tests to isolate testing logic and simulate different scenarios like successes and errors.
This guide details how to build a robust and scalable API service using Fastify and the Infobip Node.js SDK to send bulk SMS messages. We'll cover everything from project setup and core integration to error handling, security, and deployment best practices.
This service solves the common need for applications to send programmatic, high-volume SMS notifications, alerts, or marketing messages reliably. We chose Fastify for its exceptional performance and developer-friendly plugin architecture, making it ideal for high-throughput API applications. Infobip provides a powerful and globally reachable communication platform with a well-documented API and SDK, simplifying the integration process for sending SMS messages at scale.
Project Overview and Goals
Goal: Create a Fastify API endpoint that accepts a list of phone numbers and a message text, then uses the Infobip API to send that message to all recipients efficiently.
Key Features:
Technology Stack:
@infobip-api/sdk
dotenv
fastify-rate-limit
(optional, but recommended)System Architecture:
(Note: The following Mermaid diagram requires specific platform support or JavaScript libraries to render. It will appear as a raw code block in standard Markdown.)
Prerequisites:
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
Create Project Directory:
Initialize Node.js Project:
This creates a
package.json
file.Install Dependencies:
fastify
: The core web framework.@infobip-api/sdk
: The official Infobip Node.js SDK for interacting with their APIs.dotenv
: To load environment variables from a.env
file for secure credential management.Install Development Dependencies (Optional but Recommended):
nodemon
: Automatically restarts the server during development when files change.pino-pretty
: Makes Fastify's default JSON logs more human-readable during development.Configure
package.json
Scripts: Open yourpackage.json
file and add/modify thescripts
section:start
: Runs the application directly with Node.dev
: Runs the application usingnodemon
for auto-reloading and pipes logs throughpino-pretty
.""type"": ""module""
: We'll use modern ES Module syntax (import
/export
).Create Project Structure:
src/
: Contains our main application code.src/server.js
: The main entry point for our Fastify server.src/routes/
: Holds route definitions.src/plugins/
: Contains Fastify plugins (like our Infobip client setup)..env
: Stores environment variables (API keys, etc.). Never commit this file!.gitignore
: Specifies intentionally untracked files that Git should ignore.Configure
.gitignore
: Add the following lines to your.gitignore
file to prevent sensitive information and unnecessary files from being committed:Configure Environment Variables (
.env
): Open the.env
file and add your Infobip credentials. You can find these in your Infobip account dashboard under API Keys management. The Base URL is specific to your account.INFOBIP_API_KEY
: Your secret API key.INFOBIP_BASE_URL
: The specific API endpoint URL provided by Infobip for your account.SENDER_ID
: The phone number or alphanumeric ID that will appear as the sender of the SMS. This often needs to be registered/approved by Infobip depending on the destination country regulations.PORT
: The port your Fastify server will listen on. Defaults to 3000 if not set.LOG_LEVEL
: Controls the logging verbosity.Why Environment Variables? Storing credentials directly in code is a major security risk. Environment variables allow you to configure the application differently for development, testing, and production without changing the code, and keep secrets out of version control.
dotenv
loads these variables intoprocess.env
during development (when called early inserver.js
). In production, these variables are typically set directly in the deployment environment.2. Implementing Core Functionality (Infobip Plugin)
We'll create a Fastify plugin to initialize and encapsulate the Infobip SDK client, making it reusable across our application.
Create the Infobip Plugin: Open
src/plugins/infobip.js
and add the following code:dotenv
being called inserver.js
to populateprocess.env
.new Infobip(...)
: Initializes the SDK client using the API key and base URL.AuthType.ApiKey
explicitly tells the SDK how to authenticate.fastify.decorate('infobip', ...)
: Attaches the initializedinfobipClient
to the Fastify instance (and request/reply objects) under the nameinfobip
. This makes it accessible in our route handlers viafastify.infobip
orrequest.infobip
.fp(infobipPlugin, ...)
: Wraps the plugin function usingfastify-plugin
. This ensures that decorators added by this plugin (like.infobip
) are available globally within the Fastify instance, not just within the scope of the plugin itself.3. Building the API Layer (Broadcast Route)
Now, let's create the API endpoint that will receive broadcast requests and use our Infobip plugin.
Create the Broadcast Route Handler: Open
src/routes/broadcast.js
and add the following:broadcastSchema
using Fastify's built-in AJV support. This automatically validates incoming request bodies. Thepattern
uses double backslashes (\\
) as required within a JavaScript string literal to represent literal backslashes for the regex engine.to
array into thedestinations
array structure required by the Infobip SDK'ssend
method.senderId
: We use theSENDER_ID
from the environment variables (loaded inserver.js
).fastify.infobip.channels.sms.send(payload)
makes the API call.202 Accepted
along with thebulkId
and initial message statuses from Infobip's synchronous response.try...catch
block handles potential errors, logging details and attempting to forward specific Infobip error information.4. Integrating with Third-Party Services (Infobip Setup)
This section focuses on configuring the Fastify application to use the Infobip service correctly, building upon the initial setup in Section 1.
Obtain Infobip Credentials:
xxxxxx.api.infobip.com
).+14155550100
) based on your target countries and regulations. Check the ""Numbers"" or ""Senders"" section in the portal. Registration/approval might be required.Configure Environment Variables:
.env
file (for local development) or your production environment variables include the correctINFOBIP_API_KEY
,INFOBIP_BASE_URL
, andSENDER_ID
.Secure API Keys:
.gitignore
file (Step 1.7) prevents committing the.env
file..env
file. Set the environment variables (INFOBIP_API_KEY
,INFOBIP_BASE_URL
,SENDER_ID
,PORT
,LOG_LEVEL
, etc.) directly through your hosting platform's configuration interface (e.g., Heroku Config Vars, AWS Secrets Manager, Kubernetes Secrets). This keeps secrets out of your codebase and version control.Plugin and Route Usage:
src/plugins/infobip.js
) readsINFOBIP_API_KEY
andINFOBIP_BASE_URL
fromprocess.env
to initialize the SDK.src/routes/broadcast.js
) readsSENDER_ID
fromprocess.env
.Fallback Mechanisms (Consideration):
5. Implementing Error Handling, Logging, and Retry Mechanisms
Fastify uses Pino for efficient logging. We've added basic error handling; let's refine it.
Logging:
fastify.log.info/error
calls add context.pino-pretty
(vianpm run dev
) enhances readability.LOG_LEVEL
environment variable (info
,warn
,error
).Error Handling Strategy:
catch
block insrc/routes/broadcast.js
logs Infobip's detailed error (error.response.data
) and forwards the status code and details to the client.500 Internal Server Error
.Retry Mechanisms (Advanced):
202 Accepted
.Testing Error Scenarios:
.env
or mocking the SDK in tests (Section 13).6. Creating a Database Schema and Data Layer (Optional Enhancement)
A database (PostgreSQL, MongoDB, etc.) with an ORM (Prisma, Sequelize) can add features like storing recipient lists, tracking broadcast jobs (
bulkId
, status), message templates, and delivery statuses (via webhooks). This adds complexity. For this guide, we focus on direct API interaction.7. Adding Security Features
Protecting your API and credentials.
Environment Variables: Keep secrets out of code/git. Use platform secrets management in production (Section 4).
Input Validation: Implemented via Fastify schema (Section 3). Ensures data conforms to expectations (e.g., E.164 pattern, length limits).
Rate Limiting: Protect against abuse and control costs.
npm install fastify-rate-limit
src/server.js
:max
andtimeWindow
appropriately.HTTPS: Enforce HTTPS in production (usually handled by load balancers or PaaS).
Authentication/Authorization: Protect the
/broadcast/sms
endpoint itself.Authorization: Bearer YOUR_KEY
) usingfastify-auth
or similar.Helmet: Set security-related HTTP headers.
npm install fastify-helmet
await fastify.register(helmet);
insrc/server.js
.Dependency Updates: Regularly run
npm audit
and update dependencies (npm update
).Least Privilege (Infobip Key): Use Infobip API keys with the minimum required permissions if possible.
8. Handling Special Cases Relevant to the Domain (SMS)
SMS specifics to consider:
+14155552671
). Rely on Infobip's validation but handle their specific errors (EC_INVALID_DESTINATION_ADDRESS
).EC_ILLEGAL_SENDER
errors.notifyUrl
parameter in API call) or poll the DLR endpoint (GET /sms/1/reports
) to get final status (DELIVERED, FAILED, etc.). Webhooks are preferred for real-time updates.sendAt
parameter to schedule messages appropriately for recipient time zones, especially for marketing.9. Implementing Performance Optimizations
Making the service handle high load:
/sms/2/text/advanced
endpoint with thedestinations
array is batching. Break very large lists (millions) into multiple API calls (e.g., 1000 recipients per call), managed ideally via a job queue.202 Accepted
quickly; workers handle Infobip calls/retries/rate limiting. This is the most impactful optimization for high throughput.LOG_LEVEL
.clinic.js
to detect event loop blocks.429 Too Many Requests
.10. Adding Monitoring, Observability, and Analytics
Understanding service health and performance:
/health
endpoint checking essential services (Infobip client init, DB connection if used). Use for liveness/readiness probes. (See example insrc/server.js
Section 13).fastify-metrics
to expose Prometheus metrics (/metrics
) for request rates, latency, error rates, queue size, custom metrics (infobip_sms_sent_total
). Visualize in Grafana.@sentry/node
, etc.) for real-time error reporting and alerting.@autotelic/fastify-opentelemetry
) in complex microservice setups.bulkId
).11. Troubleshooting and Caveats
Common issues:
Infobip API Key or Base URL missing...
: Check.env
or production environment variables.AUTHENTICATION_ERROR
: Invalid API key. Verify key and ensure it's active.BAD_REQUEST
: Invalid payload. Check details:EC_INVALID_DESTINATION_ADDRESS
(bad number format),EC_INVALID_SENDER_OR_FROM_FIELD
(bad sender ID), empty/long text. Use E.164 format. Verify sender ID registration.MESSAGE_LIMIT_EXCEEDED
: Exceeded account MPS. Slow down requests (implement outbound rate limiting in workers).EC_ABSENT_SUBSCRIBER
,EC_ANTI_SPAM_REJECTION
,EC_INSUFFICIENT_FUNDS
). Verify number, check balance, sender ID validity, country regulations. Contact Infobip support withmessageId
.package.json
. Review changelogs before upgrading.notifyUrl
) or polling if final status is needed.12. Deployment and CI/CD
Getting your service into production:
Build Step: Not needed for our plain JS example unless using TypeScript (
npm run build
viatsc
) or bundling.Deployment Environments:
web: npm start
). Handles LB, HTTPS.Dockerfile
. EnsureNODE_ENV=production
,HOST=0.0.0.0
, and do not copy.env
into the image. Pass secrets via runtime environment.Process Management (PM2): Use PM2 for restarts, clustering, logging in VM/non-containerized environments.
npm install -g pm2
pm2 start src/server.js --name fastify-infobip-sms -i max
(cluster mode)pm2 list
,pm2 logs
,pm2 reload
CI/CD Pipeline (GitHub Actions Example):
.github/workflows/deploy.yml
to automate build, test, deploy on push tomain
.npm ci
(clean install), lint, test (npm test
), deploy (e.g., trigger PaaS hookcurl -X POST ${{ secrets.RENDER_DEPLOY_HOOK_URL }}
,docker push
, etc.).RENDER_DEPLOY_HOOK_URL
,DOCKER_PASSWORD
) in GitHub Actions secrets.Rollback Procedures: Use platform features (PaaS deploy history, Docker image tags, K8s rollbacks) or manual redeploy of previous versions. Consider blue-green/canary deployments.
13. Verification and Testing
Ensuring correctness and reliability:
Manual Verification (
curl
):npm run dev
curl
. Verify expected responses (202 Accepted withbulkId
, 400 Bad Request with error details).Automated Testing (Unit/Integration):
tap
(Fastify's default), Jest, or Node's test runner.npm install --save-dev tap
package.json
:""test"": ""tap test/**/*.test.js""
test/broadcast.test.js
).build
function: Modifysrc/server.js
to export abuild
function that creates the Fastify instance without starting it (see finalserver.js
code below).app.infobip.channels.sms.send
with a mock function (async (payload) => { ... return mockResponse; }
orthrow mockError;
) to isolate tests from the actual Infobip API and control scenarios.app.inject()
: Use Fastify's injection mechanism to simulate HTTP requests directly against the app instance.Test Coverage:
tap --coverage-report=html
). Aim for high coverage.Verification Checklist:
/broadcast/sms
accepts valid POSTs?bulkId
?Final Code Structure & Server Entry Point
Ensure your
src/server.js
ties everything together: