This guide provides a complete walkthrough for integrating the Plivo SMS API into a Next.js application to enable basic SMS sending capabilities. We will build a simple Next.js application with a backend API route that securely handles sending SMS messages via Plivo.
By the end of this guide, you will have a functional Next.js project capable of accepting a phone number and message text via an API endpoint and using Plivo to dispatch the SMS. This serves as a foundational building block for applications requiring notifications, alerts, or basic communication features.
Project Overview and Goals
What We're Building: A Next.js application featuring a serverless API route (/api/send-sms
). This route will accept POST requests containing a destination phone number and a message body. It will then use the Plivo Node.js SDK to send the SMS message.
Problem Solved: This guide addresses the need for developers to programmatically send SMS messages from a modern web application framework like Next.js, leveraging a reliable communication platform like Plivo. It provides a secure and straightforward method for server-side SMS dispatch.
Technologies Used:
- Next.js: A React framework for building server-side rendered and static web applications. We use its API Routes feature for backend functionality.
- Node.js: The JavaScript runtime environment Next.js is built upon.
- Plivo: A cloud communications platform providing SMS API services.
- Plivo Node.js SDK: A helper library simplifying interaction with the Plivo API.
System Architecture:
graph LR
A[User/Client] -- POST /api/send-sms --> B(Next.js API Route);
B -- Use Plivo SDK --> C(Plivo API);
C -- Sends SMS --> D(Recipient's Phone);
B -- Returns Success/Error --> A;
Prerequisites:
- Node.js (LTS version recommended) and npm (or yarn) installed.
- A Plivo account (Sign up here).
- Basic understanding of JavaScript, React, and Next.js concepts.
- A text editor (like VS Code).
- Access to a terminal or command prompt.
1. Setting up the Project
Let's start by creating a new Next.js project and installing the necessary dependencies.
-
Create a Next.js App: Open your terminal and run the following command. Replace
plivo-nextjs-sms
with your desired project name. Follow the prompts, selecting defaults is generally fine for this guide (using Pages Router for simplicity here, but concepts apply to App Router).npx create-next-app@latest plivo-nextjs-sms
-
Navigate to Project Directory:
cd plivo-nextjs-sms
-
Install Plivo Node.js SDK: Add the Plivo helper library to your project dependencies.
npm install plivo
or using yarn:
yarn add plivo
-
Set up Environment Variables: Securely store your Plivo credentials. Create a file named
.env.local
in the root of your project. Never commit this file to version control.# .env.local # Plivo Credentials - Get from Plivo Console Dashboard PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN # Plivo Sender ID - A Plivo phone number you own or a registered Alphanumeric Sender ID # For US/Canada, MUST be a Plivo phone number in E.164 format (e.g., +14155551212) PLIVO_SENDER_ID=YOUR_PLIVO_SENDER_ID_OR_NUMBER
- Purpose: Using environment variables prevents hardcoding sensitive credentials directly into your codebase.
.env.local
is Next.js's standard way to load these variables during development and is included in.gitignore
by default.
- Purpose: Using environment variables prevents hardcoding sensitive credentials directly into your codebase.
-
Verify
.gitignore
: Ensure that.env*.local
is listed in your project's.gitignore
file (create-next-app
usually adds this automatically). This prevents accidentally committing your secrets.# .gitignore (should include lines like this) # ... other entries .env*.local # ... other entries
Your basic project structure is now ready. We have Next.js set up, the Plivo SDK installed, and a secure way to manage API credentials.
2. Implementing Core Functionality (API Route)
The core logic for sending SMS will reside in a Next.js API route. This keeps your Plivo credentials and logic secure on the server-side.
-
Create the API Route File: Inside the
pages
directory, create a folder namedapi
. Insideapi
, create a file namedsend-sms.js
.- Project Structure:
plivo-nextjs-sms/ ├── pages/ │ ├── api/ │ │ └── send-sms.js <-- Our API endpoint │ ├── _app.js │ └── index.js ├── public/ ├── styles/ ├── .env.local ├── .gitignore ├── next.config.js ├── package.json └── README.md
- Project Structure:
-
Implement the API Logic: Open
pages/api/send-sms.js
and add the following code:// pages/api/send-sms.js import plivo from 'plivo'; export default async function handler(req_ res) { // 1. Ensure this is a POST request if (req.method !== 'POST') { res.setHeader('Allow'_ ['POST']); return res.status(405).json({ error: `Method ${req.method} Not Allowed` }); } // 2. Extract destination number and text from request body const { to_ text } = req.body; // 3. Basic Input Validation if (!to || !text) { return res.status(400).json({ error: 'Missing `to` or `text` field in request body' }); } // Add more robust validation as needed (e.g._ E.164 format check for 'to') // 4. Retrieve Plivo credentials and sender ID from environment variables const authId = process.env.PLIVO_AUTH_ID; const authToken = process.env.PLIVO_AUTH_TOKEN; const senderId = process.env.PLIVO_SENDER_ID; if (!authId || !authToken || !senderId) { console.error('Plivo credentials or sender ID are missing in environment variables.'); return res.status(500).json({ error: 'Server configuration error.' }); } // 5. Initialize Plivo client const client = new plivo.Client(authId_ authToken); // 6. Send the SMS using Plivo SDK try { const response = await client.messages.create({ src: senderId_ // Sender ID or Plivo number dst: to_ // Destination number in E.164 format text: text_ // Message content }); console.log('Plivo API Response:'_ response); // 7. Return success response return res.status(200).json({ message: 'SMS sent successfully!'_ plivoResponse: response_ // Contains the actual response from Plivo API }); } catch (error) { // 8. Handle Plivo API errors console.error('Error sending SMS via Plivo:'_ error); // Provide a more specific error message if possible let errorMessage = 'Failed to send SMS.'; if (error.message) { errorMessage += ` Plivo Error: ${error.message}`; } // Determine appropriate status code (e.g._ 400 for validation errors from Plivo_ 500 for others) // Plivo errors often include a status code_ but check SDK/API docs const statusCode = error.statusCode || 500; return res.status(statusCode).json({ error: errorMessage }); } }
Code Explanation:
- Line 1: Imports the installed
plivo
SDK. - Lines 4-8: Ensures the API route only accepts POST requests_ a standard practice for actions that change state or send data.
- Lines 11-16: Extracts the
to
(destination phone number) andtext
(message content) from the incoming JSON request body. Basic validation checks if they exist. Includes a note about further validation (like E.164). - Lines 19-25: Securely retrieves Plivo credentials and the sender ID from environment variables. Includes a check to ensure they are configured.
- Line 28: Initializes the Plivo client with your credentials.
- Lines 31-45: Uses a
try...catch
block to handle the SMS sending process.client.messages.create()
sends the actual request to Plivo.src
: Your Plivo number or registered Sender ID (from env vars).dst
: The recipient's number (from request body). Must be in E.164 format (e.g._ +12025551234).text
: The message content (from request body).
- Logs the Plivo response for debugging.
- Returns a 200 status code with a success message and the raw Plivo API response upon success.
- Lines 46-58: Catches any errors during the Plivo API call.
- Logs the error for server-side debugging.
- Constructs an informative error message_ including Plivo's specific error if available.
- Returns an appropriate HTTP status code (using Plivo's status code if provided_ otherwise 500) and the error message to the client.
This API route now encapsulates the core SMS sending functionality securely on the server.
3. Building a Complete API Layer
The pages/api/send-sms.js
file is our API layer for this simple application. Let's refine the documentation and testing aspects.
API Endpoint Documentation:
- Endpoint:
/api/send-sms
- Method:
POST
- Content-Type:
application/json
- Authentication: None (for this basic example). In a real application_ you would implement authentication (e.g._ JWT_ session cookies_ API keys) to protect this endpoint.
- Request Body (JSON):
{ ""to"": ""+1xxxxxxxxxx""_ ""text"": ""Your message content here"" }
to
: Required. Destination phone number in E.164 format.text
: Required. The SMS message text.
- Success Response (200 OK):
(Note: The
{ ""message"": ""SMS sent successfully!""_ ""plivoResponse"": { ""message"": ""message(s) queued""_ ""message_uuid"": [""xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx""]_ ""api_id"": ""xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"" } }
plivoResponse
object reflects the actual fields returned by the Plivo API_ which typically use snake_case likemessage_uuid
andapi_id
.) - Error Responses:
- 400 Bad Request (Input Validation):
{ ""error"": ""Missing `to` or `text` field in request body"" }
- 405 Method Not Allowed:
{ ""error"": ""Method GET Not Allowed"" }
- 500 Internal Server Error (Configuration or Plivo API Error):
or
{ ""error"": ""Server configuration error."" }
{ ""error"": ""Failed to send SMS. Plivo Error: Invalid 'dst' parameter"" }
- 400 Bad Request (Input Validation):
Testing with curl
:
Replace placeholders with your actual data and run this in your terminal while your Next.js development server is running (npm run dev
).
curl -X POST http://localhost:3000/api/send-sms \
-H ""Content-Type: application/json"" \
-d '{
""to"": ""+1RECIPIENT_PHONE_NUMBER""_
""text"": ""Hello from Next.js and Plivo!""
}'
Remember to replace +1RECIPIENT_PHONE_NUMBER
with a valid phone number (if using a trial Plivo account_ it must be a number verified in your Plivo console under Phone Numbers > Sandbox Numbers).
You should receive a JSON response indicating success or failure, and see logs in your Next.js development server console.
4. Integrating with Plivo (Credentials)
Proper integration hinges on correctly obtaining and configuring your Plivo credentials.
-
Sign Up/Log In: Go to the Plivo Console.
-
Find Auth ID and Auth Token:
- Navigate to the main dashboard after logging in.
- Locate the ""Account Info"" or similar section. Your Auth ID and Auth Token are displayed prominently here.
- Copy these values carefully.
-
Obtain a Sender ID (Plivo Number):
- To send SMS, especially to the US and Canada, you need a Plivo phone number.
- Navigate to ""Phone Numbers"" > ""Buy Numbers"" in the Plivo Console.
- Search for numbers based on country, capabilities (SMS), and type (Local, Toll-Free).
- Purchase a number suitable for your needs.
- Once purchased, the number will appear under ""Phone Numbers"" > ""Your Numbers"". Copy the full number in E.164 format (e.g.,
+14155551212
). - Note: In some countries outside the US/Canada, you might be able to register and use an Alphanumeric Sender ID (e.g., ""MyCompany""). Check Plivo's documentation for country-specific rules. For this guide, assume a Plivo number is used.
-
Update
.env.local
: Paste the copied Auth ID, Auth Token, and your Plivo Number (Sender ID) into the respective variables in your.env.local
file.# .env.local PLIVO_AUTH_ID=MAXXXXXXXXXXXXXXXXXX # Example Format PLIVO_AUTH_TOKEN=AbCdEfGhIjKlMnOpQrStUvWxYz0123456789 # Example Format PLIVO_SENDER_ID=+14155551212 # Example Format (Your actual Plivo number)
-
Restart Development Server: Crucially, after modifying
.env.local
, you must stop (Ctrl+C
) and restart your Next.js development server (npm run dev
oryarn dev
) for the changes to take effect.
Environment Variable Summary:
PLIVO_AUTH_ID
: Your unique Plivo account identifier. Used for authenticating API requests. Obtain from Plivo Console Dashboard.PLIVO_AUTH_TOKEN
: Your secret Plivo API key. Used for authenticating API requests. Obtain from Plivo Console Dashboard. Treat this like a password.PLIVO_SENDER_ID
: The identifier messages will originate from. For US/Canada, this must be a Plivo phone number you own, in E.164 format. Obtain by buying a number in the Plivo Console.
5. Error Handling, Logging, and Retry Mechanisms
Our API route includes basic error handling and logging.
Error Handling Strategy:
- Input Validation: Check for required fields (
to
,text
) early and return a400 Bad Request
. - Configuration Errors: Check for missing environment variables and return a
500 Internal Server Error
. - Plivo API Errors: Catch exceptions from the
plivo.Client
call. Log the detailed error server-side. Return an appropriate status code (Plivo's if available, otherwise 500) and a user-friendly error message (including Plivo's specific error if helpful) to the client.
Logging:
- Currently using
console.log
for successful Plivo responses andconsole.error
for configuration issues and Plivo API errors. - Production Logging: For production, integrate a dedicated logging service (e.g., Sentry, Datadog, Logtail, Axiom). These services provide structured logging, aggregation, search, and alerting capabilities. You would replace
console.log
/error
with calls to your chosen logging library.
Retry Mechanisms:
- Concept: For transient network issues or temporary Plivo service disruptions, implementing retries can improve reliability. A common strategy is exponential backoff (wait 1s, then 2s, then 4s, etc., before retrying).
- Implementation: This is beyond the scope of this basic guide. Libraries like
async-retry
can simplify this. You would wrap theclient.messages.create
call within a retry function. Be cautious about retrying errors that are clearly non-transient (e.g., invalid destination number, insufficient funds). - Example (Conceptual):
// import retry from 'async-retry'; // Hypothetical import // ... inside handler ... try { // await retry(async bail => { // bail is a function to stop retrying for certain errors const response = await client.messages.create({...}); // ... handle success ... // }, { retries: 3, factor: 2 }); // Example: 3 retries, doubling wait time } catch (error) { // ... handle final error ... }
Testing Error Scenarios:
- Bad Input: Send a
curl
request missing theto
ortext
field. Expect a 400 response. - Invalid Credentials: Temporarily modify
PLIVO_AUTH_ID
orPLIVO_AUTH_TOKEN
in.env.local
, restart the server, and send a valid request. Expect a 401 or similar authentication error from Plivo (logged server-side, likely resulting in a 500 response to the client). - Invalid Destination: Send a request with an incorrectly formatted
to
number (e.g., ""12345""). Expect a Plivo error (logged server-side, likely a 400 or 500 response to the client). - Trial Account Limit: If using a trial account, send an SMS to a non-verified number. Expect a specific Plivo error related to trial restrictions.
6. Database Schema and Data Layer
This specific guide focuses solely on the immediate action of sending an SMS via an API call and does not require a database.
If you were building a more complex application, you might use a database to:
- Store message history (sent time, recipient, status, content).
- Manage user accounts associated with SMS sending.
- Queue messages for later delivery.
- Track delivery statuses received via Plivo webhooks (for receiving messages or status updates).
Implementing a database would involve choosing a database (e.g., PostgreSQL, MongoDB), selecting an ORM or client library (e.g., Prisma, Mongoose, node-postgres), defining schemas/models, and writing data access logic. This is outside the scope of this basic sending guide.
7. Security Features
While basic, security is crucial.
- Environment Variables: As implemented, keeping credentials (
PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
) out of the codebase and in.env.local
(and ensuring.env.local
is in.gitignore
) is the most critical security step. - Server-Side Logic: All interaction with the Plivo API happens within the Next.js API route, preventing exposure of credentials to the client-side browser.
- Input Validation: The basic check for
to
andtext
prevents trivial errors.- Enhancement: Add more robust validation for the
to
field to ensure it resembles an E.164 formatted number before sending it to Plivo. Libraries likelibphonenumber-js
can help parse and validate phone numbers.
// Example using libphonenumber-js (install first: npm install libphonenumber-js) import { parsePhoneNumberFromString } from 'libphonenumber-js'; // ... inside handler ... const phoneNumber = parsePhoneNumberFromString(to); if (!phoneNumber || !phoneNumber.isValid()) { return res.status(400).json({ error: 'Invalid `to` phone number format.' }); } const formattedTo = phoneNumber.format('E.164'); // Use the validated/formatted number // ... use formattedTo in client.messages.create ...
- Enhancement: Add more robust validation for the
- Rate Limiting: To prevent abuse (e.g., a script spamming your endpoint), implement rate limiting on the API route.
- Next.js Middleware: You can use middleware to apply rate limiting logic before the API handler executes.
- Libraries: Packages like
rate-limiter-flexible
or Vercel's built-in helpers can be used. - Strategy: Limit requests per IP address or user identifier over a specific time window (e.g., 10 requests per minute).
- Authentication/Authorization: As mentioned, a real-world application should protect this endpoint. Only authenticated and authorized users should be able to trigger SMS sending. Implement mechanisms like session cookies, JWTs, or API key checks depending on your application's needs.
- HTTPS: Ensure your Next.js application is deployed and served over HTTPS to encrypt traffic between the client and your API route. Platforms like Vercel handle this automatically.
8. Handling Special Cases
- E.164 Format: Plivo strictly requires the destination number (
dst
) to be in E.164 format (e.g.,+14155551212
). Ensure any user input is correctly formatted before sending to the API. The validation example in section 7 helps here. - Sender ID Restrictions: As noted, the US and Canada require using a Plivo-owned, SMS-enabled phone number as the
src
. Other countries may allow Alphanumeric Sender IDs, but these often require pre-registration and cannot receive replies. Always check Plivo's documentation for the target country's regulations. - Trial Account Limitations: Plivo trial accounts can only send messages to phone numbers verified within the Plivo console (Sandbox Numbers). Sending to other numbers will result in an error.
- Character Limits & Encoding: Standard SMS messages have character limits (160 for GSM-7 encoding, 70 for UCS-2/Unicode). Longer messages are automatically split into multiple segments by Plivo (concatenation), which may incur additional costs. Be mindful of message length. Special characters often force UCS-2 encoding, reducing the characters per segment. Plivo handles smart encoding automatically to optimize this.
- Message Queuing: Plivo queues messages for delivery. The API response indicates the message was queued, not necessarily delivered. For delivery confirmation, you need to set up Delivery Report Webhooks (see Plivo documentation).
9. Performance Optimizations
For this simple API route, performance is typically bound by the Plivo API response time.
- Keep Logic Server-Side: As implemented, keeping Plivo interactions server-side is essential for security and avoids exposing credentials.
- Minimize Blocking: The code uses
async/await
, ensuring the Node.js event loop isn't blocked during the Plivo API call. - Connection Pooling (SDK): The Plivo SDK generally handles underlying HTTP connections efficiently.
- Caching: Caching is not directly applicable to the action of sending a unique SMS. If you were retrieving data related to SMS (e.g., message logs), caching strategies (like Redis or in-memory caching with time-to-live) could be applied to API routes fetching that data.
- Serverless Function Performance: When deployed on platforms like Vercel, consider cold starts for serverless functions. Frequent invocation keeps functions ""warm,"" reducing latency. For very low-traffic, high-latency-sensitive applications, provisioned concurrency might be an option (platform-dependent).
10. Monitoring, Observability, and Analytics
- Health Checks: The API route itself acts as a basic health check. If it returns a 2xx or expected error code (like 400 for bad input), the function is operational. You can set up external monitoring tools (e.g., UptimeRobot, Pingdom) to ping
/api/send-sms
(using a HEAD or GET request, though our current code only allows POST) or a dedicated/api/health
endpoint. - Performance Metrics (Vercel Example): If deployed on Vercel, the Vercel Analytics feature provides insights into function invocation counts, duration, error rates, and more, requiring minimal setup.
- Error Tracking: Integrate services like Sentry, Bugsnag, or Rollbar. These automatically capture unhandled exceptions and provide detailed stack traces, environment context, and alerting. Replace
console.error
with calls to your error tracking SDK. - Logging (Recap): Centralized logging (as mentioned in section 5) is crucial for observability. Searchable logs allow you to trace requests, diagnose errors, and monitor activity.
- Plivo Logs: Utilize the Plivo Console's ""Logs"" section. It provides detailed records of all API requests, message statuses (queued, sent, failed, delivered - if webhooks are set up), associated costs, and error details. This is invaluable for debugging Plivo-specific issues.
11. Troubleshooting and Caveats
- Error: Authentication Credentials Invalid:
- Cause:
PLIVO_AUTH_ID
orPLIVO_AUTH_TOKEN
in.env.local
is incorrect or missing. Environment variables are not loaded correctly. - Solution: Double-check credentials in
.env.local
against the Plivo Console. Restart the Next.js server after any changes to.env.local
. Ensure.env.local
is in the project root. Verifyprocess.env.PLIVO_AUTH_ID
is accessible within the API route code (add temporaryconsole.log
).
- Cause:
- Error: Missing
to
ortext
field:- Cause: The client request did not include the required fields in the JSON body, or they were not parsed correctly.
- Solution: Verify the
curl
command or frontend code is sending a valid JSON body with both fields. Check thereq.body
object in the API route usingconsole.log
. EnsureContent-Type: application/json
header is set in the request.
- Error: Invalid
dst
parameter / Number requires + prefix:- Cause: The
to
number provided is not in the required E.164 format. - Solution: Ensure the client sends the number including the
+
and country code (e.g.,+14155551212
). Implement server-side validation (see section 7) to check the format before sending to Plivo.
- Cause: The
- Error: Message destination number prohibited by trial account:
- Cause: Using a Plivo trial account and attempting to send SMS to a number not verified in the Sandbox Numbers section of the Plivo Console.
- Solution: Add the destination number to your Sandbox Numbers list in the Plivo Console, or upgrade to a paid Plivo account.
- Error: Insufficient credit:
- Cause: Your Plivo account balance is too low to cover the cost of the SMS.
- Solution: Add funds to your Plivo account.
- Caveat: Environment Variables Loading: Changes to
.env.local
require restarting the Next.js development server. In production deployments (like Vercel), ensure environment variables are set correctly in the deployment platform's settings. - Caveat: Sender ID Capabilities: Ensure the Plivo number used as
PLIVO_SENDER_ID
is SMS-enabled for the destination country. Check capabilities in the Plivo Console under Phone Numbers > Your Numbers.
12. Deployment and CI/CD
Deploying a Next.js application is straightforward, especially with platforms like Vercel (the creators of Next.js).
Deploying to Vercel:
- Push to Git: Ensure your project is hosted on a Git provider like GitHub, GitLab, or Bitbucket. Make sure
.env*.local
is in your.gitignore
. - Import Project: Sign up or log in to Vercel. Click ""Add New..."" > ""Project"". Import your Git repository.
- Configure Project: Vercel usually detects Next.js automatically.
- Add Environment Variables: Navigate to the project settings in Vercel. Go to the ""Environment Variables"" section. Add
PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
, andPLIVO_SENDER_ID
with their corresponding values. Ensure they are set for ""Production"" (and ""Preview""/""Development"" if needed). This is critical for the deployed application to work. - Deploy: Click the ""Deploy"" button. Vercel will build and deploy your application.
- Access: Once deployed, Vercel provides a URL (e.g.,
your-project-name.vercel.app
). Your API endpoint will be available athttps://your-project-name.vercel.app/api/send-sms
.
CI/CD (Continuous Integration/Continuous Deployment):
- Vercel's Git integration provides automatic CI/CD out-of-the-box.
- When you push changes to your connected Git branch (e.g.,
main
ormaster
), Vercel automatically triggers a new build and deployment. - Preview deployments are created for pull requests, allowing testing before merging to production.
Rollback Procedures:
- Vercel keeps a history of deployments. In the Vercel dashboard for your project, navigate to the ""Deployments"" tab. You can instantly promote a previous, successful deployment to become the current production deployment, effectively rolling back changes.
13. Verification and Testing
Manual Verification:
- Deploy: Deploy the application to Vercel (or run locally
npm run dev
). - Set Environment Variables: Ensure
PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
, andPLIVO_SENDER_ID
are correctly set (in.env.local
for local, Vercel dashboard for deployed). - Send Test Request: Use
curl
(as shown in section 3) or a tool like Postman/Insomnia to send a POST request to your/api/send-sms
endpoint (use the Vercel URL if deployed).- Use a valid, E.164 formatted destination number (a verified sandbox number if on a trial account).
- Include a test message.
- Check Response: Verify the API returns a
200 OK
status and the expected JSON success payload. - Check Recipient: Confirm the SMS message is received on the destination phone.
- Check Plivo Logs: Log in to the Plivo Console and check the ""Logs"" section to see the record of the sent message, its status, and any potential errors.
- Check Server Logs: Check the logs from your Next.js server (local terminal or Vercel deployment logs) for the console messages and any errors.
Automated Testing (Examples):
While comprehensive testing setup is extensive, here are conceptual examples:
-
Unit Test (API Route Logic): Use a testing framework like Jest to test the API handler function. Mock the
plivo
SDK to avoid actual API calls during tests.// Example using Jest (requires setup: npm install --save-dev jest @types/jest node-mocks-http) // pages/api/__tests__/send-sms.test.js (Conceptual) import handler from '../send-sms'; import { createMocks } from 'node-mocks-http'; // Helper library import plivo from 'plivo'; // Mock the Plivo client and its methods jest.mock('plivo', () => { const mockMessagesCreate = jest.fn(); return { Client: jest.fn().mockImplementation(() => ({ messages: { create: mockMessagesCreate }, })), }; }); describe('/api/send-sms handler', () => { let mockPlivoCreate; beforeEach(() => { // Reset mocks and setup environment variables for the test jest.clearAllMocks(); // Ensure the mock constructor is called to get the instance for method mocking const mockPlivoClientInstance = new plivo.Client(); mockPlivoCreate = mockPlivoClientInstance.messages.create; process.env.PLIVO_AUTH_ID = 'test-id'; process.env.PLIVO_AUTH_TOKEN = 'test-token'; process.env.PLIVO_SENDER_ID = '+15550001111'; }); afterEach(() => { // Clean up environment variables delete process.env.PLIVO_AUTH_ID; delete process.env.PLIVO_AUTH_TOKEN; delete process.env.PLIVO_SENDER_ID; }); it('should return 405 if method is not POST', async () => { const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); expect(res._getStatusCode()).toBe(405); expect(res._getJSONData().error).toContain('Method GET Not Allowed'); }); it('should return 400 if `to` or `text` is missing', async () => { const { req, res } = createMocks({ method: 'POST', body: { to: '+123' } }); // Missing text await handler(req, res); expect(res._getStatusCode()).toBe(400); expect(res._getJSONData().error).toContain('Missing `to` or `text`'); }); it('should call Plivo client and return 200 on success', async () => { const mockPlivoResponse = { message_uuid: ['some-uuid'], api_id: 'some-api-id' }; // Use snake_case matching API response mockPlivoCreate.mockResolvedValue(mockPlivoResponse); // Mock successful Plivo call const { req, res } = createMocks({ method: 'POST', body: { to: '+19998887777', text: 'Test message' }, }); await handler(req, res); expect(plivo.Client).toHaveBeenCalledWith('test-id', 'test-token'); expect(mockPlivoCreate).toHaveBeenCalledWith({ src: '+15550001111', dst: '+19998887777', text: 'Test message', }); expect(res._getStatusCode()).toBe(200); expect(res._getJSONData().message).toBe('SMS sent successfully!'); expect(res._getJSONData().plivoResponse).toEqual(mockPlivoResponse); }); it('should return 500 if Plivo call fails', async () => { const plivoError = new Error('Plivo Error'); // Optionally add statusCode to the mock error if Plivo SDK does // plivoError.statusCode = 503; mockPlivoCreate.mockRejectedValue(plivoError); // Mock failed Plivo call const { req, res } = createMocks({ method: 'POST', body: { to: '+19998887777', text: 'Test message' }, }); await handler(req, res); expect(res._getStatusCode()).toBe(500); // Default to 500 if error has no statusCode expect(res._getJSONData().error).toContain('Failed to send SMS. Plivo Error: Plivo Error'); }); });