code examples
code examples
Send SMS with Node.js and Infobip API
Learn how to send SMS messages using Node.js and the Infobip API. Complete guide covering manual HTTP requests, official SDK integration, API layer setup, error handling, and production deployment.
Send SMS with Node.js and Infobip API
Send SMS messages programmatically using Node.js and the Infobip API. This complete guide shows you how to integrate Infobip SMS functionality into your Node.js application using either manual HTTP requests with Axios or the official Infobip SDK.
You'll learn to set up your Node.js project, authenticate with the Infobip API, send SMS messages using two different approaches, create an Express API endpoint for SMS delivery, implement production-ready error handling, and deploy your SMS service securely. Whether you're building a notification system, two-factor authentication, or marketing campaigns, this guide covers everything you need to send SMS with Node.js and Infobip.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications.
- npm (or yarn): Package managers for Node.js.
- Infobip API: The communication platform service for sending SMS messages.
- Axios (Optional): A promise-based HTTP client for making requests manually.
- @infobip-api/sdk (Optional): The official Infobip Node.js SDK for simplified API interaction.
- Express (Optional): A minimal web framework for Node.js to create API endpoints.
- dotenv: A module to load environment variables from a
.envfile.
You need basic understanding of JavaScript, Node.js, and REST APIs.
Prerequisites
Ensure you have the following before you begin:
- Node.js and npm (or yarn): Install Node.js v20 (Iron - Maintenance LTS) or v22 (Jod - Active LTS) on your development machine. Download from nodejs.org. Production applications should use Active LTS or Maintenance LTS versions.
- Infobip Account: Sign up for a free trial or use your existing account at infobip.com. Free trial accounts can send SMS only to your verified phone number.
- Infobip API Key and Base URL: Get these credentials to authenticate your API requests.
Why Use Infobip for SMS Integration?
Infobip provides a robust SMS API platform for sending transactional and marketing messages at scale. Developers choose Infobip for:
Common Use Cases:
- Two-Factor Authentication (2FA): Send verification codes for secure login
- Transactional Notifications: Order confirmations, shipping updates, appointment reminders
- Marketing Campaigns: Promotional messages and customer engagement
- Alert Systems: Critical notifications and system alerts
Key Benefits:
- Global Reach: Send SMS to 190+ countries with carrier connections worldwide
- High Deliverability: Direct carrier partnerships ensure reliable message delivery
- Developer-Friendly: RESTful API with official SDKs for Node.js, Python, Java, PHP, and more
- Scalability: Handle from a few messages to millions per day
- Delivery Reports: Real-time status updates via webhooks for message tracking
This guide focuses on Node.js integration, but the concepts apply to other programming languages.
Getting Your Infobip API Credentials
Get two key pieces of information from your Infobip account:
- API Key: Authenticates your requests.
- Base URL: The specific API endpoint domain assigned to your account.
Steps to find your credentials:
- Log in to your Infobip account.
- Navigate to the homepage dashboard after login.
- Find your Base URL prominently displayed (e.g.,
xxxxx.api.infobip.com). - Click your username or profile icon in the top-right corner.
- Look for
API Keysor navigate to theDeveloperssection. - View existing API keys or generate a new one. Copy the API Key value securely. Treat this key like a password – never share it publicly or commit it to version control.
Keep these two values handy – you'll use them shortly.
1. Setting up the Project
Create a new Node.js project.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir node-infobip-sms cd node-infobip-sms -
Initialize Node.js Project: Run
npm initand follow the prompts. Accept the defaults by pressing Enter repeatedly, or customize the details. This creates apackage.jsonfile.bashnpm init -y(The
-yflag accepts all defaults) -
Install Core Dependencies: Install
dotenvto manage your API credentials securely.bashnpm install dotenv(If using yarn:
yarn add dotenv) -
Create
.gitignore: Prevent committing sensitive information and unnecessary files. Create a file named.gitignorein your project root:text# .gitignore # Dependencies node_modules/ # Environment variables .env # Log files *.log # OS generated files .DS_Store Thumbs.db -
Create
.envFile: Create a file named.envin the project root to store your sensitive Infobip credentials. Add your API Key and Base URL:dotenv# .env INFOBIP_API_KEY=YOUR_API_KEY_HERE INFOBIP_BASE_URL=YOUR_BASE_URL_HERE # Optional: Your phone number for testing (must be verified for free trial accounts) YOUR_VERIFIED_PHONE_NUMBER=YOUR_PHONE_NUMBER_HEREImportant: Replace the placeholder values with your actual Infobip API Key, Base URL, and optionally your verified phone number (include the country code, e.g.,
+15551234567).
Now your basic project structure is ready.
2. Send SMS Messages with Node.js: Two Approaches
Send SMS using two approaches:
- Approach 1: Manually construct and send HTTP requests using
axios. This provides deeper understanding of the API interaction. - Approach 2: Use the official
@infobip-api/sdk. This simplifies the process and is recommended for production use.
Approach 1: Send SMS with Axios HTTP Client
Use the axios HTTP client to directly interact with the Infobip Send SMS API endpoint (/sms/2/text/advanced).
-
Install Axios:
bashnpm install axios(If using yarn:
yarn add axios) -
Create
sendManual.js: Create a new file namedsendManual.jscontaining the logic for sending SMS.javascript// sendManual.js const axios = require('axios'); // Helper function to build the API URL const buildUrl = (domain) => { if (!domain) { throw new Error('Infobip domain (Base URL) is mandatory in config.'); } // Ensure the domain doesn't include 'https://' as we add it here const cleanDomain = domain.replace(/^https?:\/\//, ''); return `https://${cleanDomain}/sms/2/text/advanced`; }; // Helper function to build request headers const buildHeaders = (apiKey) => { if (!apiKey) { throw new Error('Infobip API Key is mandatory in config.'); } return { 'Content-Type': 'application/json', 'Accept': 'application/json', // Good practice to accept JSON responses 'Authorization': `App ${apiKey}` }; }; // Helper function to construct the request body const buildRequestBody = (destinationNumber, message, sender = 'InfoSMS') => { // Basic validation: optional '+' followed by 10-15 digits. if (!/^\+?\d{10,15}$/.test(destinationNumber)) { console.warn(`Destination number ""${destinationNumber}"" may not be in the correct international format (e.g., +15551234567).`); // Depending on requirements, you might throw an error here instead. } if (!message || typeof message !== 'string' || message.trim() === '') { throw new Error('Message content cannot be empty.'); } const destinationObject = { to: destinationNumber }; const messageObject = { destinations: [destinationObject], text: message, // Optional: Sender ID ('from' field). Using defaults like 'InfoSMS'. // Note: Alphanumeric Sender IDs often require registration and may not work reliably everywhere without it. See 'Special Cases' section. from: sender }; return { messages: [messageObject] }; }; // Helper function to parse successful responses const parseSuccessResponse = (axiosResponse) => { const responseBody = axiosResponse.data; // Handle cases where the response might not contain messages (unlikely for success, but defensive) if (!responseBody || !responseBody.messages || responseBody.messages.length === 0) { return { success: true, data: responseBody, warning: ""Successful response but no message details found."" }; } const singleMessageResponse = responseBody.messages[0]; return { success: true, messageId: singleMessageResponse.messageId, status: singleMessageResponse.status.name, groupName: singleMessageResponse.status.groupName, // More descriptive status group description: singleMessageResponse.status.description, bulkId: responseBody.bulkId // Include bulkId if present }; }; // Helper function to parse error responses const parseFailedResponse = (axiosError) => { let errorInfo = { success: false, errorMessage: axiosError.message, errorDetails: null, statusCode: null }; if (axiosError.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx const responseBody = axiosError.response.data; errorInfo.statusCode = axiosError.response.status; if (responseBody && responseBody.requestError && responseBody.requestError.serviceException) { errorInfo.errorMessage = responseBody.requestError.serviceException.text || 'Unknown Infobip service exception'; errorInfo.errorDetails = responseBody; // Include the full error body } else { errorInfo.errorMessage = `Infobip API Error (Status ${errorInfo.statusCode}): ${axiosError.response.statusText}`; errorInfo.errorDetails = responseBody || axiosError.response.statusText; } } else if (axiosError.request) { // The request was made but no response was received errorInfo.errorMessage = 'No response received from Infobip API. Check network connectivity or Base URL.'; } else { // Something happened in setting up the request that triggered an Error errorInfo.errorMessage = `Axios request setup error: ${axiosError.message}`; } return errorInfo; }; // Main function to send SMS const sendSmsManually = async (config, destinationNumber, message) => { try { // Validate input configuration if (!config || !config.domain || !config.apiKey) { throw new Error('Configuration object with domain and apiKey is required.'); } if (!destinationNumber) throw new Error('destinationNumber parameter is mandatory'); if (!message) throw new Error('message parameter is mandatory'); const url = buildUrl(config.domain); const requestBody = buildRequestBody(destinationNumber, message); const headers = buildHeaders(config.apiKey); console.log(`Sending SMS manually to ${destinationNumber} via ${url}`); const response = await axios.post(url, requestBody, { headers: headers }); return parseSuccessResponse(response); } catch (error) { const parsedError = parseFailedResponse(error); console.error('Error sending SMS manually:', parsedError); return parsedError; // Return the structured error object } }; module.exports = { sendSmsManually };Why this structure? Breaking down the logic into smaller, focused functions (URL building, header construction, body construction, response parsing) makes the code cleaner, easier to test, and more maintainable. The main
sendSmsManuallyfunction orchestrates these steps and handles the overall success/error flow usingasync/awaitfor better readability. We added basic input validation and more robust error parsing. -
Create
index.jsfor Testing: Create a file namedindex.jsin the project root to test thesendManual.jsmodule.javascript// index.js require('dotenv').config(); // Load environment variables from .env file const { sendSmsManually } = require('./sendManual'); // Import the manual function // --- Configuration --- const infobipConfig = { domain: process.env.INFOBIP_BASE_URL, apiKey: process.env.INFOBIP_API_KEY, }; const recipientPhoneNumber = process.env.YOUR_VERIFIED_PHONE_NUMBER; // Use the number from .env const messageText = `Hello from Node.js (Manual Axios)! Time: ${new Date().toLocaleTimeString()}`; // --- Validate Configuration --- if (!infobipConfig.domain || !infobipConfig.apiKey) { console.error('ERROR: INFOBIP_BASE_URL and INFOBIP_API_KEY must be set in the .env file.'); process.exit(1); // Exit if configuration is missing } if (!recipientPhoneNumber) { console.error('ERROR: YOUR_VERIFIED_PHONE_NUMBER must be set in the .env file for testing.'); process.exit(1); } // --- Send SMS --- console.log(`Attempting to send SMS to: ${recipientPhoneNumber}`); sendSmsManually(infobipConfig, recipientPhoneNumber, messageText) .then(result => { console.log('Manual Send Result:', JSON.stringify(result, null, 2)); if(result.success) { console.log(`SMS submitted successfully! Message ID: ${result.messageId}, Status: ${result.status}`); } else { console.error(`SMS sending failed. Error: ${result.errorMessage}`); } }) .catch(error => { // This catch is for unexpected errors not handled within sendSmsManually console.error('Unexpected error in index.js:', error); }); -
Run the Test: Execute the
index.jsfile from your terminal:bashnode index.jsYou should see output indicating the attempt and then either a success message with details or an error message. Check your phone for the SMS! Remember, free trial accounts can typically only send to the phone number used during signup.
Approach 2: Send SMS with Infobip Official Node.js SDK
The official SDK (@infobip-api/sdk) abstracts away the direct HTTP calls, providing a cleaner interface.
-
Install the SDK:
bashnpm install @infobip-api/sdk(If using yarn:
yarn add @infobip-api/sdk) -
Create
sendSdk.js: Create a new file namedsendSdk.js.javascript// sendSdk.js const { Infobip, AuthType } = require('@infobip-api/sdk'); let infobipClient; // Keep client instance reusable // Function to initialize the client (can be called once) const initializeInfobipClient = (config) => { if (!config || !config.domain || !config.apiKey) { throw new Error('Configuration object with domain and apiKey is required for SDK initialization.'); } if (!infobipClient) { console.log(""Initializing Infobip SDK client...""); infobipClient = new Infobip({ baseUrl: config.domain, // SDK expects the full base URL apiKey: config.apiKey, authType: AuthType.ApiKey, // Specify API Key authentication }); } return infobipClient; }; // Main function to send SMS using the SDK const sendSmsWithSdk = async (config, destinationNumber, message, sender = 'InfoSDK') => { try { const client = initializeInfobipClient(config); // Get or initialize client if (!destinationNumber) throw new Error('destinationNumber parameter is mandatory'); if (!message) throw new Error('message parameter is mandatory'); // Basic validation: optional '+' followed by 10-15 digits. if (!/^\+?\d{10,15}$/.test(destinationNumber)) { console.warn(`Destination number ""${destinationNumber}"" may not be in the correct international format (e.g., +15551234567).`); } console.log(`Sending SMS via SDK to ${destinationNumber}`); // Construct the payload using the SDK's structure const sendSmsPayload = { messages: [{ destinations: [{ to: destinationNumber }], // Optional: Sender ID ('from' field). Using defaults like 'InfoSDK'. // Note: Alphanumeric Sender IDs often require registration and may not work reliably everywhere without it. See 'Special Cases' section. from: sender, text: message, }], // You can add more options here like bulkId, notifyUrl etc. // See SDK documentation for details. }; // Use the client instance to send the SMS const infobipResponse = await client.channels.sms.send(sendSmsPayload); // Parse the SDK response (structure is slightly different from manual) const responseData = infobipResponse.data; // SDK wraps response in 'data' if (!responseData || !responseData.messages || responseData.messages.length === 0) { return { success: true, data: responseData, warning: ""Successful response but no message details found."" }; } const singleMessageResponse = responseData.messages[0]; return { success: true, messageId: singleMessageResponse.messageId, status: singleMessageResponse.status.name, groupName: singleMessageResponse.status.groupName, description: singleMessageResponse.status.description, bulkId: responseData.bulkId }; } catch (error) { console.error('Error sending SMS with SDK:', error.response ? JSON.stringify(error.response.data, null, 2) : error.message); // Parse SDK error structure (often nested in error.response.data) let errorMessage = error.message; let errorDetails = null; let statusCode = error.response ? error.response.status : null; if (error.response && error.response.data && error.response.data.requestError && error.response.data.requestError.serviceException) { errorMessage = error.response.data.requestError.serviceException.text || 'Unknown Infobip SDK service exception'; errorDetails = error.response.data; } else if (error.response && error.response.data) { errorMessage = `Infobip SDK Error (Status ${statusCode})`; errorDetails = error.response.data; } return { success: false, errorMessage: errorMessage, errorDetails: errorDetails, statusCode: statusCode }; } }; module.exports = { sendSmsWithSdk, initializeInfobipClient }; // Export bothWhy this structure? The SDK simplifies interaction significantly. We initialize the client once (or check if it exists) for efficiency. The
sendSmsWithSdkfunction focuses on building the correct payload structure expected by the SDK method (client.channels.sms.send) and handling its specific response and error formats. -
Update
index.jsfor SDK Testing: Modifyindex.jsto use the SDK function.javascript// index.js require('dotenv').config(); // Choose one implementation to test: // const { sendSmsManually } = require('./sendManual'); const { sendSmsWithSdk } = require('./sendSdk'); // Import the SDK function // --- Configuration --- const infobipConfig = { domain: process.env.INFOBIP_BASE_URL, apiKey: process.env.INFOBIP_API_KEY, }; const recipientPhoneNumber = process.env.YOUR_VERIFIED_PHONE_NUMBER; // Change message slightly to distinguish SDK test const messageText = `Hello from Node.js (Official SDK)! Time: ${new Date().toLocaleTimeString()}`; // --- Validate Configuration --- if (!infobipConfig.domain || !infobipConfig.apiKey) { console.error('ERROR: INFOBIP_BASE_URL and INFOBIP_API_KEY must be set in the .env file.'); process.exit(1); } if (!recipientPhoneNumber) { console.error('ERROR: YOUR_VERIFIED_PHONE_NUMBER must be set in the .env file for testing.'); process.exit(1); } // --- Send SMS --- console.log(`Attempting to send SMS to: ${recipientPhoneNumber} using the SDK`); // Call the SDK function sendSmsWithSdk(infobipConfig, recipientPhoneNumber, messageText) .then(result => { console.log('SDK Send Result:', JSON.stringify(result, null, 2)); if(result.success) { console.log(`SDK: SMS submitted successfully! Message ID: ${result.messageId}, Status: ${result.status}`); } else { console.error(`SDK: SMS sending failed. Error: ${result.errorMessage}`); } }) .catch(error => { console.error('Unexpected error in index.js:', error); }); -
Run the Test:
bashnode index.jsAgain, check your console output and your phone.
Choosing an Approach
- Manual (
axios): Good for learning exactly how the API works, offers maximum control over the request, fewer dependencies if you already useaxios. However, it requires more boilerplate code and manual maintenance if the API changes. - Official SDK (
@infobip-api/sdk): Recommended for most production scenarios. It's easier to use, reduces code volume, handles authentication and request structure internally, and is maintained by Infobip, ensuring compatibility with API updates.
For robustness and ease of maintenance, using the official SDK is generally the preferred approach.
3. Create a REST API Endpoint for SMS Sending (Optional)
Expose SMS functionality via a simple REST API using Express.
-
Install Express:
bashnpm install express(If using yarn:
yarn add express) -
Create
server.js: Create a file namedserver.js. We'll use the SDK implementation here for brevity.javascript// server.js require('dotenv').config(); const express = require('express'); const { sendSmsWithSdk, initializeInfobipClient } = require('./sendSdk'); // Use SDK const app = express(); const port = process.env.PORT || 3000; // Use environment variable or default // --- Middleware --- app.use(express.json()); // Enable parsing JSON request bodies // --- Configuration --- const infobipConfig = { domain: process.env.INFOBIP_BASE_URL, apiKey: process.env.INFOBIP_API_KEY, }; // --- Validate Configuration on Startup --- if (!infobipConfig.domain || !infobipConfig.apiKey) { console.error('FATAL ERROR: INFOBIP_BASE_URL and INFOBIP_API_KEY must be set in the environment variables.'); process.exit(1); } // --- Initialize Infobip Client --- // Initialize client once when server starts try { initializeInfobipClient(infobipConfig); console.log(""Infobip client initialized successfully.""); } catch (error) { console.error(""FATAL ERROR: Failed to initialize Infobip client:"", error.message); process.exit(1); } // --- API Routes --- app.get('/', (req, res) => { res.send('SMS Service is running. Use POST /send to send an SMS.'); }); app.post('/send', async (req, res) => { const { to, message } = req.body; // Get recipient number and message from request body // --- Basic Input Validation --- if (!to || !message) { // Use backticks for field names return res.status(400).json({ success: false, error: 'Missing required fields: `to` (phone number) and `message`.' }); } // Add more robust validation for phone number format if needed if (typeof to !== 'string' || typeof message !== 'string' || message.trim() === '') { return res.status(400).json({ success: false, error: 'Invalid input format. `to` and `message` must be non-empty strings.' }); } console.log(`Received request to send SMS to: ${to}`); try { // Assuming infobipConfig is correctly populated from environment variables const result = await sendSmsWithSdk(infobipConfig, to, message); if (result.success) { console.log(`API: SMS submitted successfully for ${to}. Message ID: ${result.messageId}`); // Return only relevant success info to the client res.status(200).json({ success: true, messageId: result.messageId, status: result.status, description: result.description }); } else { console.error(`API: Failed to send SMS to ${to}. Error: ${result.errorMessage}`, result.errorDetails || ''); // Return a generic server error or specific error if safe res.status(result.statusCode || 500).json({ success: false, error: result.errorMessage || 'Failed to send SMS due to an internal error.', // Optionally include errorDetails in non-production environments for debugging details: process.env.NODE_ENV !== 'production' ? result.errorDetails : undefined }); } } catch (error) { // Catch unexpected errors during the API call processing console.error(`API: Unexpected error processing /send request for ${to}:`, error); res.status(500).json({ success: false, error: 'An unexpected server error occurred.' }); } }); // --- Start Server --- app.listen(port, () => { console.log(`SMS API server listening on port ${port}`); console.log(`Infobip Base URL configured: ${infobipConfig.domain}`); }); -
Run the Server:
bashnode server.jsThe server will start, typically on port 3000.
-
Test the API Endpoint: You can use
curlor a tool like Postman to send a POST request.Using
curl: ReplaceYOUR_PHONE_NUMBERwith the recipient's number (must be verified for free trial).bashcurl -X POST http://localhost:3000/send \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""YOUR_PHONE_NUMBER"", ""message"": ""Hello from the Express API!"" }'Expected Response (Success):
json{ ""success"": true, ""messageId"": ""SOME_MESSAGE_ID_FROM_INFOBIP"", ""status"": ""PENDING_ACCEPTED"", ""description"": ""Message accepted and pending processing."" }Expected Response (Failure - e.g., missing field):
json{ ""success"": false, ""error"": ""Missing required fields: `to` (phone number) and `message`."" }
4. Infobip API Authentication and Configuration
The core integration is complete:
-
Configuration: API Key (
INFOBIP_API_KEY) and Base URL (INFOBIP_BASE_URL) are stored securely in the.envfile and loaded usingdotenv. -
Authentication: Handled either via the
Authorization: App YOUR_API_KEYheader (manual approach) or automatically by the SDK when configured withAuthType.ApiKey. -
Dashboard Navigation: As detailed in the ""Getting Your Infobip API Credentials"" section.
-
Environment Variables:
INFOBIP_API_KEY: Your secret key for API authentication. Obtain from Infobip portal API Keys section. Format: String.INFOBIP_BASE_URL: Your account-specific API domain. Obtain from Infobip portal dashboard. Format: String (e.g.,xxxxx.api.infobip.com).YOUR_VERIFIED_PHONE_NUMBER: (Optional, for testing) The phone number linked to your Infobip trial account. Format: String (e.g.,+15551234567).PORT: (Optional, for server) The port number for the Express server to listen on. Format: Number.
-
Fallback Mechanisms: For simple SMS sending, implement basic fallback by retrying requests (see Error Handling below). For critical systems, consider alternative providers or channels.
5. Production-Ready Error Handling and Logging
Implement solid error handling for robust applications.
-
Error Handling Strategy:
- Both
sendManual.jsandsendSdk.jsincludetry...catchblocks. - Specific Infobip errors (usually returned in the response body on non-2xx status codes) are parsed and returned in a consistent
{ success: false, errorMessage: '...', errorDetails: '...', statusCode: ... }format. - Network errors or request setup issues are also caught.
- The API layer (
server.js) catches errors during request processing and returns appropriate HTTP status codes (400 for bad input, 500 for server errors, or the status code from Infobip if available).
- Both
-
Logging:
- We are using basic
console.logandconsole.error. - For production, replace these with a structured logging library like
pinoorwinston. This enables:- Different log levels (debug, info, warn, error).
- Outputting logs in JSON format for easier parsing by log management systems.
- Writing logs to files or external services.
- Conceptual Example (using Pino):
Note: This requires installing
pino(npm install pino) and initializing the logger.bashnpm install pino pino-pretty # pino-pretty for development loggingjavascript// --- Conceptual Pino Integration --- // At the top of server.js (replace console with logger) const pino = require('pino'); // Initialize Pino Logger const logger = pino({ level: process.env.LOG_LEVEL || 'info', // Use pino-pretty for human-readable logs in development, JSON in production transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined, }); // Example Usage (replace console.* calls): // logger.info(`SMS API server listening on port ${port}`); // logger.error({ err: error, to }, `API: Unexpected error processing /send request.`); // --- End Conceptual Pino Integration --- // --- Example usage within server.js --- // Replace console.log(...) with logger.info(...) // Replace console.error(...) with logger.error(...) // Inside /send route error handling: // logger.error({ err: result.errorDetails, to }, `API: Failed to send SMS. Error: ${result.errorMessage}`); // ... // logger.error({ err: error, to }, `API: Unexpected error processing /send request.`); // Start server (using logger) app.listen(port, () => { logger.info(`SMS API server listening on port ${port}`); logger.info(`Infobip Base URL configured: ${infobipConfig.domain}`); });
- We are using basic
-
Retry Mechanisms:
- Sending an SMS is often idempotent (sending the same message twice might be acceptable or undesirable depending on context).
- For transient network errors or specific temporary Infobip issues (e.g., 5xx status codes), you might implement retries.
- Use libraries like
async-retryor implement a manual loop with exponential backoff (wait longer between each retry). - Caution: Be careful not to retry on permanent errors (like invalid API keys or invalid phone numbers - 4xx errors) or you'll waste resources.
- Conceptual Example (using
async-retry): Note: This requires installingasync-retry(npm install async-retry) and assumes aloggerinstance is available.bashnpm install async-retryjavascript// --- Conceptual Async-Retry Integration (within sendSdk.js) --- const retry = require('async-retry'); // Assumes a 'logger' instance (like Pino from above) is available in this scope // Inside sendSmsWithSdk function, wrap the API call: // try { // ... setup before call ... const sendOperation = async (bail, attemptNumber) => { // bail(error) is used to stop retrying on permanent errors const client = initializeInfobipClient(config); // Ensure client is ready try { logger.info(`Attempt ${attemptNumber}: Sending SMS via SDK to ${destinationNumber}`); const infobipResponse = await client.channels.sms.send(sendSmsPayload); return infobipResponse; // Success! Return the response } catch (error) { // Decide if the error is permanent (e.g., 4xx client errors) if (error.response && error.response.status >= 400 && error.response.status < 500) { logger.error(`Permanent error detected (Status ${error.response.status}). Bailing out.`, error); bail(error); // Don't retry 4xx errors } // Log other errors and allow retry throw error; // Important: re-throw the error so retry() catches it } }; // Retry logic const response = await retry(sendOperation, { retries: 3, // Number of retries factor: 2, // Exponential backoff factor minTimeout: 1000, // Initial delay ms maxTimeout: 5000, // Max delay ms onRetry: (error, attemptNumber) => { logger.warn({ err: error }, `Attempt ${attemptNumber} failed. Retrying...`); } }); // Parse success response (same as before) const responseData = response.data; // ... rest of success parsing ... if (!responseData || !responseData.messages || responseData.messages.length === 0) { return { success: true, data: responseData, warning: ""Successful response but no message details found."" }; } const singleMessageResponse = responseData.messages[0]; return { success: true, messageId: singleMessageResponse.messageId, status: singleMessageResponse.status.name, groupName: singleMessageResponse.status.groupName, description: singleMessageResponse.status.description, bulkId: responseData.bulkId }; } catch (error) { // This catch block now handles errors after all retries failed logger.error({ err: error }, 'Error sending SMS with SDK after retries'); // Parse SDK error structure (same as before) let errorMessage = error.message; let errorDetails = null; let statusCode = error.response ? error.response.status : null; if (error.response && error.response.data && error.response.data.requestError && error.response.data.requestError.serviceException) { errorMessage = error.response.data.requestError.serviceException.text || 'Unknown Infobip SDK service exception'; errorDetails = error.response.data; } else if (error.response && error.response.data) { errorMessage = `Infobip SDK Error (Status ${statusCode})`; errorDetails = error.response.data; } return { success: false, errorMessage: errorMessage, errorDetails: errorDetails, statusCode: statusCode }; } // }; // End of sendSmsWithSdk function // module.exports = { sendSmsWithSdk, initializeInfobipClient }; // --- End Conceptual Async-Retry Integration ---
Frequently Asked Questions
How to send SMS with Node.js and Infobip?
You can send SMS messages with Node.js and Infobip using either manual HTTP requests with Axios or the official Infobip Node.js SDK. The SDK simplifies the process and is recommended for production, while the manual approach is good for learning. Both methods require your Infobip API Key and Base URL, stored securely in a .env file.
What is the Infobip Node.js SDK?
The Infobip Node.js SDK (@infobip-api/sdk) streamlines interaction with the Infobip API. It handles authentication and request structure internally, reducing boilerplate code compared to manual HTTP requests. This approach is generally preferred for production environments.
Why does Infobip need an API key?
Infobip uses API keys for authentication and security, ensuring only authorized applications can access and use the SMS service. Treat your API key like a password and never expose it publicly or in version control.
When should I use the manual Axios approach?
The manual Axios approach is best when you need deep control over HTTP requests or are learning how the Infobip API works. However, for production, the official SDK is generally recommended for easier maintenance and API compatibility.
Can I create an API endpoint for sending SMS?
Yes, you can easily create a simple API endpoint using Express.js to expose your SMS sending functionality. This allows other applications to trigger SMS messages through your Node.js service.
How to get Infobip API key and base URL?
Log in to your Infobip account, navigate to the dashboard for your Base URL, and find your API Key under your profile (or the 'Developers' section). Keep these credentials secure.
What is the purpose of dotenv in this project?
The dotenv module loads environment variables from a .env file, enabling you to store sensitive credentials like API keys securely outside of your codebase.
How to install the Infobip Node.js SDK?
Use your preferred Node.js package manager: npm install @infobip-api/sdk (npm) or yarn add @infobip-api/sdk (yarn). This adds the SDK to your project dependencies.
What are the prerequisites for sending SMS with Infobip?
You need Node.js and npm (or yarn) installed, an active Infobip account (a free trial is sufficient for testing), your Infobip API Key, and Base URL.
Why use Express.js for the API layer?
Express.js simplifies creating web servers and API endpoints in Node.js. It's lightweight, flexible, and widely used, making it a convenient choice for exposing SMS functionality.
How to handle errors when sending SMS?
Implement try...catch blocks in your SMS sending logic to handle potential errors, including network issues, invalid credentials, or problems with Infobip's service. Log errors using a structured logging library and consider retry mechanisms for transient errors.
How to structure a Node.js project for sending SMS?
Create separate modules for manual sending (sendManual.js), SDK sending (sendSdk.js), and an API server (server.js). Use dotenv for environment variables, and consider a structured logger for production.
Where do I find my Infobip base URL?
Your Infobip Base URL is displayed prominently on your account dashboard after you log in to the Infobip portal. It's a domain specific to your account for accessing the API.
What are the benefits of using an SDK?
SDKs like Infobip's Node.js SDK simplify API interaction by handling authentication, request formatting, and response parsing. They reduce boilerplate code and are maintained for API compatibility.