code examples
code examples
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:
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).
npmoryarnpackage 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.
-
Create a New Next.js App: Open your terminal and run the following command. Replace
infobip-nextjs-guidewith your preferred project name.bashnpx create-next-app@latest infobip-nextjs-guideFollow the prompts (you can accept the defaults).
-
Navigate to Project Directory:
bashcd infobip-nextjs-guide -
Install Infobip Node.js SDK: We'll use the official SDK for interacting with the Infobip API.
bashnpm install @infobip-api/sdkor if using yarn:
bashyarn add @infobip-api/sdk -
Set Up Environment Variables: Sensitive credentials like API keys should never be hardcoded. We'll use environment variables.
- Create a file named
.env.localin 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.localfile). - 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 (
fromfield 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.localis added to your.gitignorefile (this is default increate-next-app) to prevent accidentally committing secrets.
- Create a file named
-
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.mdWe 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.
-
Create the API Route File: Create a new file:
pages/api/send-sms.js -
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:
- Method Check: Ensures only
POSTrequests are accepted. - Input Validation: Performs basic checks on
toandtext. Includes a very basic E.164 format check. Robust validation is recommended for production. - Payload Construction: Creates the
smsPayloadfor the SDK, usingfromfrom the request or falling back to environment variables/defaults. - Sending SMS: Calls
infobipClient.channels.sms.send(smsPayload)within atry...catchblock. Includes logging. - Success Response: Returns
200 OKwith success message and key details from the Infobip response. - 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
zodorjoifor 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 likelibphonenumber-jsto 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:
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
fromfield: 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):
{
"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):
{
"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:
-
Instantiation:
javascriptconst 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.ApiKeyensures the SDK uses the correctAuthorization: App YOUR_API_KEYheader format. -
API Call Structure:
javascriptconst 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.
- Constructing the correct HTTP request (e.g.,
Handling API Keys Securely:
- Environment Variables: Using
.env.localfor 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.localor 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...catchfor external calls. - Log errors effectively (see below).
- Return meaningful HTTP status codes (4xx client, 5xx server/dependency).
- Provide clear
success: falseandmessage/errorDetailsin 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 oferror.response,error.response.data, etc., to handle network errors or unexpected structures more gracefully.
Logging:
- Development:
console.log/console.erroris 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.responsemight be undefined). Do not retry 4xx errors (bad input, auth failure). - Implementation: Use libraries like
async-retryor implement manually.
Conceptual Retry Logic (Manual):
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):
// 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 neededImplementing 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-flexibleor platform features (Vercel, Cloudflare, API Gateway).- Example (
rate-limiter-flexibleconceptual):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 ...
- Example (
- 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) innext.config.jsfor 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 likelibphonenumber-jsfor 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 (
fromfield):- 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
messagesarray 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/healthendpoint returning200 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): CheckINFOBIP_API_KEY,INFOBIP_BASE_URLaccuracy and environment loading. Ensure Base URL includeshttps://if needed. Restart server after.env.localchanges. - 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
curlusing 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 starton EC2, Droplets, etc. Manage environment variables via system or.envfiles (usingdotenvlibrary 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):
- Create
.github/workflows/deploy.yml:
# .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' }}- Configure GitHub Secrets: Add
VERCEL_TOKEN,VERCEL_ORG_ID,VERCEL_PROJECT_IDin your repository's Settings > Secrets and variables > Actions. - 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:
- Deploy to staging.
- Configure staging environment variables.
- Use
curl/Postman/frontend to hit the deployed/api/send-smsendpoint. - Check: Receive
200 OKwith Infobip IDs. - Check: SMS received on the target phone (respect free trial limits).
- Check: Invalid inputs return
400 Bad Request. - Check: Invalid API key returns
401 Unauthorized. - 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/sdkto 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.) }); - Mock
-
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.