code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

How to Send SMS with Infobip Node.js SDK in Next.js

Build a Next.js SMS integration with Infobip's official Node.js SDK. Learn how to create secure API endpoints, handle errors, validate phone numbers, and deploy production-ready SMS functionality.

Build a Next.js SMS integration with Infobip's Node.js SDK to send SMS messages programmatically. You'll create a secure API endpoint within your Next.js app that handles SMS delivery via Infobip's cloud communications platform.

Integrate SMS notifications, alerts, marketing campaigns, or two-factor authentication (2FA) directly into your Next.js web applications using the official Infobip API.

Goal: Create a Next.js API route (/api/send-sms) that accepts a phone number and message text, then uses the Infobip Node.js SDK to send SMS messages with proper error handling and validation.

Problem Solved: Leverage Infobip's robust SMS infrastructure within Next.js, abstracting direct HTTP calls and managing authentication securely.

Technologies Used:

  • Next.js: A popular React framework for building server-rendered or statically generated web applications, including API routes that run server-side Node.js code.
  • Node.js: The JavaScript runtime environment used by Next.js for its backend features.
  • Infobip: A global cloud communications platform providing APIs for various channels, including SMS.
  • @infobip-api/sdk: The official Infobip Node.js SDK for interacting with their APIs.
  • Environment Variables: For securely storing sensitive API credentials.

System Architecture:

mermaid
graph LR
    A[User's Browser] -- Makes POST request --> B(Next.js App / Frontend);
    B -- Calls API route --> C(Next.js API Route `/api/send-sms`);
    C -- Uses Infobip SDK --> D(Infobip API);
    D -- Sends SMS --> E(User's Phone);
    D -- Sends Response --> C;
    C -- Sends Response --> B;
    B -- Displays Result --> A;

Prerequisites:

  • A free or paid Infobip account (free trial includes 100 SMS to up to 5 verified recipients).
  • Node.js v18 or later (LTS recommended).
  • npm or yarn package manager.
  • Basic understanding of JavaScript, Node.js, Next.js, and REST APIs.

Expected Outcome: A functional Next.js application with an API endpoint capable of sending SMS messages via Infobip, ready for integration into larger projects.


1. Setting up the Next.js SMS Project

Start by creating a new Next.js project and installing the Infobip Node.js SDK dependency.

  1. Create a New Next.js App: Open your terminal and run the following command. Replace infobip-nextjs-guide with your preferred project name.

    bash
    npx create-next-app@latest infobip-nextjs-guide

    Follow the prompts (you can accept the defaults).

  2. Navigate to Project Directory:

    bash
    cd infobip-nextjs-guide
  3. Install Infobip Node.js SDK: We'll use the official SDK for interacting with the Infobip API.

    bash
    npm install @infobip-api/sdk

    or if using yarn:

    bash
    yarn add @infobip-api/sdk
  4. Set Up Environment Variables: Sensitive credentials like API keys should never be hardcoded. We'll use environment variables.

    • Create a file named .env.local in the root of your project directory.
    • Add the following lines to .env.local, replacing the placeholder values with your actual Infobip credentials:
    plaintext
    # .env.local
    
    # Your Infobip API Key (obtain from Infobip portal)
    INFOBIP_API_KEY=YOUR_INFOBIP_API_KEY
    
    # Your Infobip Base URL (obtain from Infobip portal, e.g., xyz.api.infobip.com)
    INFOBIP_BASE_URL=YOUR_INFOBIP_BASE_URL
    
    # Optional: Default Sender ID (must be registered/approved by Infobip)
    # INFOBIP_SENDER_ID=InfoSMS
    • Obtaining Credentials:

      • Log in to your Infobip Portal.
      • Navigate to the Developers section or API Keys management area (exact navigation may vary slightly).
      • Generate a new API Key if you don't have one. Copy this key immediately and store it securely (in your .env.local file).
      • Your Base URL is usually displayed alongside your API key or on the API documentation landing page within the portal after you log in. It's specific to your account.
      • The Sender ID (from field in the API) often needs to be registered with Infobip, especially for alphanumeric senders, depending on country regulations. You might start with a default numeric sender provided or approved by Infobip during signup.
    • Security: Ensure .env.local is added to your .gitignore file (this is default in create-next-app) to prevent accidentally committing secrets.

  5. Project Structure: Your basic structure will look like this (simplified):

    infobip-nextjs-guide/ ├── pages/ │ ├── api/ # API routes live here │ │ └── send-sms.js # Our SMS sending endpoint │ └── index.js # Example frontend page (optional) ├── public/ ├── styles/ ├── .env.local # Infobip credentials (DO NOT COMMIT) ├── .gitignore ├── package.json └── README.md

    We place our server-side logic within the pages/api/ directory, leveraging Next.js's API routes feature.


2. Implementing the SMS Sending API Endpoint

Create the API route that handles SMS sending logic with the Infobip SDK.

  1. Create the API Route File: Create a new file: pages/api/send-sms.js

  2. Implement the API Handler: Add the following code to pages/api/send-sms.js.

    javascript
    // pages/api/send-sms.js
    
    import { Infobip, AuthType } from '@infobip-api/sdk';
    
    // Instantiate the Infobip client
    // Ensure environment variables are loaded correctly
    // Note: Base URL might need 'https://' prefix if not included in the env var
    const infobipClient = new Infobip({
      baseUrl: process.env.INFOBIP_BASE_URL,
      apiKey: process.env.INFOBIP_API_KEY,
      authType: AuthType.ApiKey,
    });
    
    export default async function handler(req, res) {
      // 1. Only allow POST requests
      if (req.method !== 'POST') {
        res.setHeader('Allow', ['POST']);
        return res.status(405).json({ message: `Method ${req.method} Not Allowed` });
      }
    
      // 2. Basic Input Validation
      const { to, text, from } = req.body; // `from` is optional in request body if default is set
    
      if (!to || typeof to !== 'string') {
        return res.status(400).json({ message: 'Missing or invalid \'to\' field (phone number).' });
      }
      if (!text || typeof text !== 'string') {
        return res.status(400).json({ message: 'Missing or invalid \'text\' field (message content).' });
      }
    
      // Basic E.164 format check.
      // WARNING: This regex is very basic. It allows numbers without '+' and doesn't
      // fully validate E.164 structure or length limits precisely.
      // Consider using a dedicated library like 'libphonenumber-js' for robust validation.
      // See Section 3 for more details.
      const phoneRegex = /^\+?[1-9]\d{1,14}$/;
      if (!phoneRegex.test(to)) {
          return res.status(400).json({ message: 'Invalid \'to\' phone number format. Should resemble E.164 (e.g., +14155552671).' });
      }
    
      // Use default sender from env var if not provided in request, otherwise use request body 'from'
      const senderId = from || process.env.INFOBIP_SENDER_ID || 'InfoSMS'; // Fallback sender
    
      // 3. Construct the SMS Payload for Infobip SDK
      const smsPayload = {
        messages: [
          {
            destinations: [{ to: to }],
            from: senderId,
            text: text,
            // Add other options like flash, validityPeriod, notifyUrl etc. if needed
            // See Infobip docs: https://www.infobip.com/docs/api/channels/sms/send-sms-message
          },
        ],
        // bulkId: 'YOUR_CUSTOM_BULK_ID', // Optional: For tracking batches
        // tracking: { track: 'SMS', type: 'MY_CAMPAIGN' }, // Optional: For analytics
      };
    
      try {
        // 4. Send the SMS using the Infobip SDK
        console.log('Sending SMS payload:', JSON.stringify(smsPayload, null, 2));
    
        // --- Integration point for Retry Logic (See Section 5) ---
        // Replace the direct call below with the retry function if implemented:
        // const infobipResponse = await sendSmsWithRetry(smsPayload);
        // --- Original Direct Call ---
        const infobipResponse = await infobipClient.channels.sms.send(smsPayload);
    
        console.log('Infobip API Response:', JSON.stringify(infobipResponse.data, null, 2));
    
        // 5. Respond with Success
        // Extract relevant info like messageId and status
        const firstMessageResult = infobipResponse.data.messages?.[0];
        return res.status(200).json({
          success: true,
          message: 'SMS sent successfully.',
          infobipResponse: {
              bulkId: infobipResponse.data.bulkId,
              messageId: firstMessageResult?.messageId,
              status: firstMessageResult?.status?.name, // e.g., PENDING_ACCEPTED
              statusGroup: firstMessageResult?.status?.groupName, // e.g., PENDING
          },
        });
    
      } catch (error) {
        // 6. Handle Errors from Infobip SDK or Network
        console.error('Error sending SMS via Infobip:', error);
    
        // Extract more detailed error info if available from Infobip's response structure
        // Assumption: Error structure follows Axios pattern (error.response.data)
        // and Infobip's specific error format (requestError.serviceException).
        // This might be brittle if SDK/API changes error formats.
        // Consider adding checks: if (error.response && error.response.data && ...)
        const errorResponse = error.response?.data || {};
        const serviceException = errorResponse.requestError?.serviceException;
    
        let statusCode = 500;
        let errorMessage = 'Failed to send SMS.';
        let errorDetails = serviceException?.text || error.message || 'Unknown error';
    
        if (error.response?.status) {
            statusCode = error.response.status; // Use HTTP status from Infobip if available
            if (statusCode === 401) errorMessage = 'Infobip authentication failed. Check API Key and Base URL.';
            if (statusCode === 400) errorMessage = 'Bad request. Check payload structure or parameters.';
        }
    
        // Specific Infobip error message parsing
         if (serviceException?.messageId === 'UNAUTHORIZED') {
             statusCode = 401;
             errorMessage = `Infobip Authentication Error: ${serviceException.text}`;
             errorDetails = serviceException;
         } else if (serviceException) {
             // statusCode = 400; // Often indicates bad input if not auth error - use status from response if available
             errorMessage = `Infobip Service Error: ${serviceException.text}`;
             errorDetails = serviceException;
         }
    
        return res.status(statusCode).json({
          success: false,
          message: errorMessage,
          errorDetails: errorDetails, // Provide details for debugging
        });
      }
    }

Code Explanation:

  1. Method Check: Ensures only POST requests are accepted.
  2. Input Validation: Performs basic checks on to and text. Includes a very basic E.164 format check. Robust validation is recommended for production.
  3. Payload Construction: Creates the smsPayload for the SDK, using from from the request or falling back to environment variables/defaults.
  4. Sending SMS: Calls infobipClient.channels.sms.send(smsPayload) within a try...catch block. Includes logging.
  5. Success Response: Returns 200 OK with success message and key details from the Infobip response.
  6. Error Handling: Catches errors, logs details, attempts to parse specific Infobip error information, and returns an appropriate HTTP error status code (4xx/5xx) with a descriptive message.

3. Validating Phone Numbers and Testing Your SMS API

Your Next.js API route (/api/send-sms) serves as the API layer. Enhance it with robust validation and comprehensive testing.

Request Validation (Improvements):

  • Schema Validation: Use a library like zod or joi for robust request body schema validation (e.g., check types, required fields, lengths).
  • Phone Number Validation: The current regex (/^\+?[1-9]\d{1,14}$/) is basic. Infobip generally expects E.164 format (+ followed by country code and number, no spaces/dashes). For production, use a dedicated library like libphonenumber-js to parse and validate numbers more accurately based on country rules.
  • Message Length: Validate message length against SMS character limits (160 for GSM-7, 70 for UCS-2) to provide immediate feedback, although Infobip handles segmentation.

Authentication/Authorization:

  • Protect this endpoint. Options include session/token verification, API keys for service-to-service calls, or IP whitelisting.

Testing the Endpoint:

Run your Next.js dev server (npm run dev or yarn dev). Use tools like curl or Postman.

Using curl:

bash
curl -X POST http://localhost:3000/api/send-sms \
-H "Content-Type: application/json" \
-d '{
  "to": "+14155552671",
  "text": "Hello from Next.js and Infobip! Test @ [Current Time]"
}'
  • Replace +14155552671: Use your registered number for Infobip free trials, or any valid number for paid accounts.
  • Optional from field: To specify a sender ID different from the default/environment variable, add it to the JSON payload: {"to": "...", "text": "...", "from": "YourSenderID"}. Ensure this sender ID is approved/valid in your Infobip account.

Expected Success Response (JSON):

json
{
  "success": true,
  "message": "SMS sent successfully.",
  "infobipResponse": {
    "bulkId": "some-bulk-id-from-infobip",
    "messageId": "some-message-id-from-infobip",
    "status": "PENDING_ACCEPTED",
    "statusGroup": "PENDING"
  }
}

Expected Error Response (JSON - Example: Invalid API Key):

json
{
  "success": false,
  "message": "Infobip Authentication Error: Invalid login details",
  "errorDetails": {
    "messageId": "UNAUTHORIZED",
    "text": "Invalid login details"
  }
}

4. Configuring the Infobip SDK for Node.js

Review the key configuration aspects for the Infobip Node.js SDK authentication and API integration:

  1. Instantiation:

    javascript
    const infobipClient = new Infobip({
      baseUrl: process.env.INFOBIP_BASE_URL,
      apiKey: process.env.INFOBIP_API_KEY,
      authType: AuthType.ApiKey, // Specify API Key authentication
    });

    This creates the client, reading credentials securely from environment variables. AuthType.ApiKey ensures the SDK uses the correct Authorization: App YOUR_API_KEY header format.

  2. API Call Structure:

    javascript
    const infobipResponse = await infobipClient.channels.sms.send(smsPayload);

    The SDK simplifies the interaction. It handles:

    • Constructing the correct HTTP request (e.g., POST /sms/2/text/advanced).
    • Setting necessary headers (Authorization, Content-Type, Accept).
    • Sending the JSON payload.
    • Parsing the response or throwing an error.

Handling API Keys Securely:

  • Environment Variables: Using .env.local for local development and platform environment variables (Vercel, Netlify, AWS, etc.) for deployment is the standard.
  • Secrets Management Systems: For enhanced security in production, especially with cloud providers, use services like AWS Secrets Manager, Google Secret Manager, Azure Key Vault, or HashiCorp Vault. These inject secrets securely at runtime.
  • Never commit secrets to Git. Ensure .env.local or other secret files are in .gitignore.

Fallback Mechanisms:

  • For high-availability needs, consider a secondary SMS provider or channel (like email) if Infobip encounters issues. This requires:
    • Advanced error handling to differentiate between Infobip errors and network problems.
    • Integrating another provider's API/SDK.
    • Logic to trigger the fallback based on specific error conditions.
    • This adds significant complexity.

5. Error Handling and Retry Logic for SMS Delivery

Implement robust error handling, structured logging, and automatic retry mechanisms for failed SMS delivery.

Consistent Strategy:

  • Use try...catch for external calls.
  • Log errors effectively (see below).
  • Return meaningful HTTP status codes (4xx client, 5xx server/dependency).
  • Provide clear success: false and message/errorDetails in responses.
  • Be mindful of the error structure assumption (error.response.data.requestError.serviceException). While common for Infobip API errors via the SDK, consider adding checks for the existence of error.response, error.response.data, etc., to handle network errors or unexpected structures more gracefully.

Logging:

  • Development: console.log/console.error is acceptable.
  • Production: Use a structured logging library (e.g., pino, winston) outputting JSON. This integrates well with log aggregation systems (Datadog, Logz.io, ELK, CloudWatch Logs).
  • Log Content: Include timestamp, severity, request ID (if possible), error message, stack trace, relevant Infobip error details (status code, message ID, text), and potentially sanitized request data.

Retry Mechanisms:

  • Implement retries for transient issues (network errors, temporary Infobip 5xx errors).
  • Strategy: Exponential backoff with jitter (increase delay between retries, add randomness).
  • When to Retry: On 5xx errors or network errors (where error.response might be undefined). Do not retry 4xx errors (bad input, auth failure).
  • Implementation: Use libraries like async-retry or implement manually.

Conceptual Retry Logic (Manual):

javascript
async function sendSmsWithRetry(payload, maxRetries = 3, currentAttempt = 1) {
  try {
    // Use the globally configured client
    return await infobipClient.channels.sms.send(payload);
  } catch (error) {
    const statusCode = error.response?.status;
    // Retry on 5xx or network errors (no status code from response)
    if (currentAttempt < maxRetries && (!statusCode || statusCode >= 500)) {
      const delay = Math.pow(2, currentAttempt - 1) * 1000 + Math.random() * 1000; // Exponential backoff + jitter
      console.warn(`Attempt ${currentAttempt} failed for SMS sending. Retrying in ${delay.toFixed(0)}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      return sendSmsWithRetry(payload, maxRetries, currentAttempt + 1);
    } else {
      // Non-retryable error or max retries reached
      console.error(`SMS sending failed after ${currentAttempt} attempts or due to non-retryable error.`);
      throw error; // Re-throw the error to be caught by the main handler
    }
  }
}

// --- How to integrate into the main handler (Section 2) ---
// Inside the `try` block of the `handler` function, replace:
// const infobipResponse = await infobipClient.channels.sms.send(smsPayload);
// WITH:
// const infobipResponse = await sendSmsWithRetry(smsPayload);
// The `catch` block in the main `handler` will then catch the final error after retries fail.

6. Database Schema and Data Layer (Optional Extension)

While not required for basic sending, storing SMS data is common:

  • Track Status: Save messageId, bulkId. Use Infobip Delivery Report webhooks to update status (DELIVERED, FAILED, etc.).
  • Audit Log: Record sent messages (recipient, content, timestamp, status).
  • Contact/Campaign Management: Link messages to users or campaigns.

Example Schema (Conceptual - using Prisma):

prisma
// schema.prisma

datasource db {
  provider = ""postgresql"" // or mysql, sqlite
  url      = env(""DATABASE_URL"")
}

generator client {
  provider = ""prisma-client-js""
}

model SmsMessage {
  id            String   @id @default(cuid())
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  infobipMessageId String?  @unique // Store the ID from Infobip
  infobipBulkId    String?  // Store the Bulk ID if applicable
  recipient      String   @index // The 'to' phone number (index for lookups)
  sender         String   // The 'from' sender ID
  messageText    String   @db.Text // The message content
  status         String   @default(""PENDING"") @index // e.g., PENDING, SENT, DELIVERED, FAILED, REJECTED
  statusDetails  String?  // Store detailed error message if failed
  sentAt         DateTime? // Timestamp when the API call was made

  // Optional relations
  // userId    String?
  // user      User?    @relation(fields: [userId], references: [id])
  // campaignId String?
  // campaign  Campaign? @relation(fields: [campaignId], references: [id])
}

// Define User, Campaign models as needed

Implementing this requires setting up Prisma (or another ORM), defining the schema, migrating the database (npx prisma migrate dev), and adding database interactions to your API route or a separate service layer.


7. Adding Security Features

Protect your API endpoint and application:

  • Input Validation and Sanitization: Use robust schema validation (zod). Sanitize user input if displayed elsewhere (prevent XSS).
  • Protect Against Common Vulnerabilities:
    • Authentication/Authorization: Secure the endpoint (Section 3).
    • Rate Limiting: Prevent abuse and cost overruns. Use libraries like rate-limiter-flexible or platform features (Vercel, Cloudflare, API Gateway).
      • Example (rate-limiter-flexible conceptual):
        javascript
        // Install: npm install rate-limiter-flexible
        import { RateLimiterMemory } from 'rate-limiter-flexible';
        const limiterOptions = { points: 10, duration: 60 }; // Max 10 requests per IP per minute
        const rateLimiter = new RateLimiterMemory(limiterOptions);
        
        // In handler, before main logic:
        try {
          // Use req.socket.remoteAddress or a more reliable identifier like user ID if authenticated
          const clientIdentifier = req.socket.remoteAddress || 'unknown_ip';
          await rateLimiter.consume(clientIdentifier);
        } catch (rejRes) {
          // Log rate limit exceeded event
          console.warn(`Rate limit exceeded for identifier: ${clientIdentifier}`);
          return res.status(429).json({ message: 'Too Many Requests' });
        }
        // ... rest of handler logic ...
    • Secrets Management: Securely handle API keys (Section 4).
    • Dependency Vulnerabilities: Regularly update dependencies (npm audit fix, yarn audit) and use scanning tools (Snyk).
  • Security Headers: Configure standard security headers (X-Content-Type-Options, Strict-Transport-Security, Content-Security-Policy) in next.config.js for your frontend pages.

8. SMS Best Practices: Phone Formatting, Encoding, and Regulations

Follow these SMS best practices for phone number formatting, character encoding, and compliance:

  • Phone Number Formatting: Enforce E.164 format (+CountryCodeNumber) strictly. Use libraries like libphonenumber-js for parsing/validation.
  • Message Length & Encoding:
    • GSM-7 (standard): 160 chars/segment.
    • UCS-2 (Unicode/emojis): 70 chars/segment.
    • Long messages are segmented and billed per segment. Validate length client/server-side.
  • Sender ID (from field):
    • Alphanumeric: Requires pre-registration, regulations vary by country, may not support replies.
    • Numeric/Short Code: May allow replies, regulations vary.
    • Consult Infobip docs/support for country-specific rules.
  • Country-Specific Regulations: Adhere to local laws regarding content, opt-in, sender IDs.
  • Delivery Reports: Implement webhooks for real-time status updates (requires a public endpoint).
  • Time Zones: API times are typically UTC. Handle conversions appropriately for user display.

9. Optimizing SMS Performance for High Volume

Implement these performance optimizations for bulk SMS sending:

  • Batching: Send multiple messages (different recipients or content) in one API call using the messages array in the payload. This reduces HTTP overhead.
  • Asynchronous Processing: For non-time-critical SMS, queue the sending task (BullMQ, Redis, SQS) and respond immediately to the user ("SMS queued"). Process jobs in the background.
  • Connection Pooling: The SDK should manage this. If using raw HTTP clients, ensure keep-alive connections are used.
  • Caching: Cache templates or user data before calling the API, not typically the API response itself for transactional SMS.
  • Load Testing: Use tools (k6, artillery) to test your endpoint's performance under load and identify bottlenecks.

10. Adding Monitoring, Observability, and Analytics

Gain visibility into your production system:

  • Health Checks: /api/health endpoint returning 200 OK.
  • Performance Metrics: Track Infobip API call duration, endpoint latency (/api/send-sms), resource usage (CPU/memory). Use Vercel Analytics, Datadog, New Relic, Prometheus/Grafana.
  • Error Tracking: Integrate Sentry, Bugsnag, Rollbar to capture and analyze exceptions in API routes.
  • Logging Aggregation: Centralize structured logs (Datadog, Logz.io, CloudWatch Logs, Loki).
  • Dashboards: Visualize key metrics: SMS success/failure rate, endpoint latency (p50, p99), error rates, Infobip API duration.
  • Alerting: Set up alerts for high error rates, high latency, specific Infobip errors (e.g., auth failures), resource exhaustion.

11. Troubleshooting Common Infobip SMS Integration Issues

Resolve common Infobip API integration problems:

  • Authentication Errors (401 Unauthorized): Check INFOBIP_API_KEY, INFOBIP_BASE_URL accuracy and environment loading. Ensure Base URL includes https:// if needed. Restart server after .env.local changes.
  • Invalid Destination Number (400 Bad Request): Verify E.164 format (+...). Check Infobip portal logs for specifics (e.g., EC_INVALID_DESTINATION_ADDRESS).
  • Free Trial Limitations: Can only send to your registered phone number.
  • Sender ID Issues (REJECTED...): Use registered/approved sender IDs. Check country rules. Check account balance for paid accounts.
  • Message Content Rejected (REJECTED_...): Review content for spam triggers or regulatory violations.
  • Rate Limiting by Infobip: Implement client-side rate limiting/queuing or contact Infobip for higher throughput.
  • SDK Issues: Check SDK documentation/version. Look for known issues on GitHub.
  • Network Issues: Implement retries. Check server connectivity to Infobip endpoints.

Debugging Tips:

  • Infobip Portal Logs: Essential for detailed request/delivery status.
  • Log Request/Response: Log the exact payload sent to Infobip and the full response received.
  • Isolate: Test with curl using the simplest valid payload.

12. Deploying Your Next.js SMS Application

Deploy your Next.js Infobip SMS application to production:

Deployment Platforms:

  • Vercel/Netlify: Easy deployment via Git push. Configure Infobip environment variables in platform settings.
  • Docker: Containerize the app. Deploy to AWS (ECS, EKS, App Runner), Google Cloud (Cloud Run, GKE), Azure (App Service, AKS), etc. Inject environment variables at runtime.
  • Node.js Server: Run next build && next start on EC2, Droplets, etc. Manage environment variables via system or .env files (using dotenv library for production builds).

Environment Variables in Production:

  • Set INFOBIP_API_KEY, INFOBIP_BASE_URL, INFOBIP_SENDER_ID (if used) in the deployment environment's configuration. Do not deploy .env.local.
  • Consider separate Infobip API keys for dev/staging/production.

CI/CD Pipeline (Example using GitHub Actions for Vercel):

  1. Create .github/workflows/deploy.yml:
yaml
# .github/workflows/deploy.yml
name: Deploy to Vercel

on:
  push:
    branches:
      - main # Or your production branch

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4 # Use a recent version

      # Optional: Add build, lint, test steps before deploying
      # - name: Setup Node.js
      #   uses: actions/setup-node@v4
      #   with:
      #     node-version: '18' # Specify your Node version
      # - name: Install Dependencies
      #   run: npm ci
      # - name: Run Lint Check
      #   run: npm run lint
      # - name: Run Tests
      #   # Ensure INFOBIP env vars are NOT needed or are mocked for unit tests
      #   run: npm test

      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v20 # Or official Vercel CLI action
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required: Vercel API token
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} # Required: Vercel team/org ID
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} # Required: Vercel project ID
          # Optional: Deploy to production environment (if main branch)
          # vercel-prod: ${{ github.ref == 'refs/heads/main' }}
  1. Configure GitHub Secrets: Add VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID in your repository's Settings > Secrets and variables > Actions.
  2. Vercel Environment Variables: Ensure INFOBIP_API_KEY, INFOBIP_BASE_URL, etc., are set in the Vercel Project Settings UI (for Production, Preview, Development environments as needed).

Rollback Procedures:

  • Vercel/Netlify allow instant rollbacks to previous deployments via their dashboards.
  • For container/server setups, use blue-green deployments or revert to a previous stable Docker image tag/commit.

13. Verification and Testing

Ensure your integration is reliable:

Manual Verification Steps:

  1. Deploy to staging.
  2. Configure staging environment variables.
  3. Use curl/Postman/frontend to hit the deployed /api/send-sms endpoint.
  4. Check: Receive 200 OK with Infobip IDs.
  5. Check: SMS received on the target phone (respect free trial limits).
  6. Check: Invalid inputs return 400 Bad Request.
  7. Check: Invalid API key returns 401 Unauthorized.
  8. Check: Review logs (deployment platform & Infobip portal).

Automated Testing:

  • Unit Tests (API Route Logic): Use Jest to test pages/api/send-sms.js.

    • Mock @infobip-api/sdk to avoid actual API calls.
    • Test valid requests, missing/invalid inputs, SDK success, SDK errors (various types).
    • Assert response status code, body, and SDK mock calls.

    Example Unit Test Snippet (using Jest):

    javascript
    // pages/api/__tests__/send-sms.test.js
    import { createMocks } from 'node-mocks-http';
    import sendSmsHandler from '../send-sms'; // Adjust path relative to test file
    import { Infobip } from '@infobip-api/sdk'; // Import to enable mocking
    
    // Mock the Infobip SDK constructor and its chained methods
    jest.mock('@infobip-api/sdk', () => {
        const mockSend = jest.fn(); // Create a mock function for 'send'
        return {
            Infobip: jest.fn().mockImplementation(() => ({
                channels: {
                    sms: {
                        send: mockSend, // Assign the mock function here
                    },
                },
            })),
            AuthType: { ApiKey: 'ApiKey' }, // Provide AuthType used
            // Expose the mockSend function for use in tests if needed directly
            // This approach might vary based on exact mocking needs
            __mockSend: mockSend,
        };
    });
    
    // Get a reference to the mock 'send' function
    // This line might need adjustment based on how Jest resolves the mock instance.
    // Often, you interact with the mock instance created *inside* the test.
    // Let's get the mock via the imported module after it's been mocked.
    const { __mockSend: mockInfobipSend } = require('@infobip-api/sdk');
    
    describe('/api/send-sms handler', () => {
        beforeEach(() => {
            // Reset mock state and environment variables before each test
            mockInfobipSend.mockClear();
            jest.resetModules(); // May be needed if the handler imports the SDK at top level
            process.env.INFOBIP_API_KEY = 'fake-key';
            process.env.INFOBIP_BASE_URL = 'fake.api.infobip.com';
            process.env.INFOBIP_SENDER_ID = 'InfoSMS'; // Default sender for tests
        });
    
        // Add test cases here...
        // Example: Test successful SMS sending
        test('should return 200 on successful SMS send', async () => {
            const mockSuccessResponse = {
                data: {
                    bulkId: 'test-bulk-id',
                    messages: [
                        {
                            messageId: 'test-message-id',
                            status: { groupName: 'PENDING', name: 'PENDING_ACCEPTED' },
                            to: '+14155552671',
                        },
                    ],
                },
            };
            mockInfobipSend.mockResolvedValue(mockSuccessResponse);
    
            const { req, res } = createMocks({
                method: 'POST',
                body: {
                    to: '+14155552671',
                    text: 'Test message',
                },
            });
    
            await sendSmsHandler(req, res);
    
            expect(res._getStatusCode()).toBe(200);
            expect(JSON.parse(res._getData())).toEqual(expect.objectContaining({
                success: true,
                message: 'SMS sent successfully.',
                infobipResponse: expect.objectContaining({
                    messageId: 'test-message-id',
                    status: 'PENDING_ACCEPTED',
                }),
            }));
            expect(mockInfobipSend).toHaveBeenCalledTimes(1);
            expect(mockInfobipSend).toHaveBeenCalledWith(expect.objectContaining({
                messages: expect.arrayContaining([
                    expect.objectContaining({
                        destinations: [{ to: '+14155552671' }],
                        text: 'Test message',
                        from: 'InfoSMS', // Check default sender is used
                    }),
                ]),
            }));
        });
    
        // Example: Test missing 'to' field
        test('should return 400 if \'to\' field is missing', async () => {
            const { req, res } = createMocks({
                method: 'POST',
                body: {
                    text: 'Test message', // 'to' field is missing
                },
            });
    
            await sendSmsHandler(req, res);
    
            expect(res._getStatusCode()).toBe(400);
            expect(JSON.parse(res._getData())).toEqual({ message: 'Missing or invalid \'to\' field (phone number).' });
            expect(mockInfobipSend).not.toHaveBeenCalled();
        });
    
        // Add more tests for other scenarios (invalid 'text', Infobip API errors, etc.)
    });
  • Integration Tests: Test the API route without mocking the SDK, using test Infobip credentials (if available) or a dedicated test environment. This verifies actual interaction but incurs costs/uses quotas.

  • End-to-End (E2E) Tests: Use tools like Cypress or Playwright to simulate user interaction (if you build a frontend) that triggers the API call and potentially verifies SMS reception (difficult to automate fully).

Frequently Asked Questions

How to send SMS with Next.js and Infobip?

Create a Next.js API route (/api/send-sms) that accepts recipient number and message text. Use the Infobip Node.js SDK with your API key to send the SMS via this endpoint. The SDK simplifies interaction with the Infobip API, abstracting away direct HTTP requests, handling authentication, and managing responses from the Infobip platform.

What is the Infobip Node.js SDK?

The Infobip Node.js SDK (@infobip-api/sdk) is a library that simplifies interacting with the Infobip API from your Node.js applications. It handles authentication, HTTP requests, response parsing, and error management, making it easier to integrate Infobip services into your Next.js project or other Node.js-based applications.

Why use environment variables for Infobip API key?

Storing sensitive credentials like API keys directly in your code is a security risk. Environment variables (.env.local for development, platform settings in production) provide a secure way to manage these, preventing accidental exposure in version control and simplifying deployment across different environments.

When should I use Infobip's SMS API?

Infobip's SMS API is ideal for integrating various messaging functionalities into your applications, including sending notifications, alerts, running marketing campaigns, and implementing two-factor authentication (2FA). Its versatility and robust infrastructure make it a suitable choice for handling diverse messaging needs.

Can I send SMS messages in bulk with Infobip?

Yes, the Infobip API supports sending bulk SMS messages efficiently. Utilize the 'messages' array in the API request payload to include multiple recipients or different message content within a single API call. This optimizes performance and minimizes overhead compared to individual requests.

How to validate phone numbers for Infobip SMS?

While the example provides a basic regex, using a dedicated library like `libphonenumber-js` is strongly recommended for production. It ensures accurate E.164 formatting and validation according to international phone number rules. This minimizes rejected messages due to invalid numbers.

What are best practices for error handling with the Infobip SDK?

Implement comprehensive error handling using try...catch blocks around API calls. Log errors with relevant details (timestamps, Infobip error codes) using a structured logging library like Pino or Winston. Consider retry mechanisms with exponential backoff and jitter for transient errors (network issues, temporary 5xx responses).

How to integrate SMS sending with my database?

You can store SMS message data (recipient, message content, status, Infobip message ID) in a database. This allows tracking message status, creating audit logs, and associating SMS with user accounts or marketing campaigns. Use an ORM like Prisma to define your schema and manage database interactions efficiently.

How to improve SMS sending performance with Next.js?

For higher volumes, leverage the Infobip API's bulk sending capabilities. Queue non-critical SMS messages for asynchronous processing using a task queue like BullMQ or Redis. Implement appropriate caching strategies for frequently used data (e.g., message templates).

What security measures should I consider when integrating with Infobip?

Secure your API endpoint with authentication/authorization mechanisms. Implement rate limiting to prevent abuse. Use environment variables or secrets management services for API keys. Sanitize user inputs. Regularly update dependencies and conduct security audits. Configure appropriate security headers for your application.

How to troubleshoot Infobip authentication errors in Next.js?

Double-check the accuracy of your INFOBIP_API_KEY and INFOBIP_BASE_URL in your environment variables. Ensure your base URL includes the 'https://' prefix if required by Infobip. Restart your development server after making changes to .env.local to ensure the updated values are loaded.

Why are my SMS messages being rejected by Infobip?

Several factors can cause rejections, including invalid destination numbers (ensure E.164 format), issues with the sender ID (verify registration/approval), message content that violates Infobip's policies, or exceeding rate limits. Check Infobip's portal logs for specific error codes.

What is the system architecture for sending SMS using Next.js and Infobip?

The user interacts with the Next.js frontend, which makes a POST request to the /api/send-sms API route. This route utilizes the Infobip Node.js SDK to communicate with the Infobip API, which then sends the SMS to the user's phone. The response from Infobip is relayed back to the frontend.

How to implement retry logic for SMS sending?

Use a library like 'async-retry' or implement custom logic with exponential backoff and jitter. This involves retrying failed API calls after increasing delays, adding randomness to avoid synchronized retries. Focus on retrying 5xx errors and network issues, not 4xx errors.

How can I monitor my Infobip integration in production?

Set up health checks for your API endpoint. Track key metrics like API call duration, endpoint latency, and error rates using monitoring tools (Datadog, New Relic). Use structured logging and log aggregation systems. Configure alerts for critical events such as high error rates or authentication failures.