Developer Guide: Sending Plivo MMS with NestJS
This guide provides a complete walkthrough for building a production-ready service to send Multimedia Messaging Service (MMS) messages using Plivo's API within a NestJS application. We'll cover everything from initial project setup to deployment and verification.
This implementation solves the need for programmatically sending rich media messages (images, GIFs) to users' phones, essential for notifications, marketing alerts, or user engagement features that require more than plain text.
Project Overview and Goals
Goal: To create a robust NestJS API endpoint capable of accepting requests to send MMS messages via Plivo.
Core Functionality:
- Receive API requests specifying recipient, message text, and media URLs.
- Validate incoming request data.
- Interact securely with the Plivo API using their Node.js SDK.
- Send MMS messages including specified media.
- Provide basic error handling and logging.
Technologies Used:
- NestJS: A progressive Node.js framework for building efficient, reliable server-side applications. Chosen for its modular architecture, dependency injection, and TypeScript support.
- Plivo Node.js SDK: Simplifies interaction with the Plivo REST API.
- TypeScript: Enhances code quality and maintainability.
- dotenv / @nestjs/config: For managing environment variables securely.
- class-validator / class-transformer: For robust request data validation.
System Architecture:
The system involves a client application making a request to the NestJS API. The NestJS application, specifically the MMS controller, processes this request. It utilizes a dedicated Plivo service (within NestJS) to handle the interaction with the Plivo Cloud API. The Plivo API then communicates with the carrier network to deliver the MMS message to the recipient's phone.
Prerequisites:
- Node.js (LTS version recommended) and npm/yarn installed.
- A Plivo account (Sign up here).
- A Plivo phone number enabled for MMS (available for US/Canada). You can purchase one via the Plivo console.
- Basic understanding of NestJS concepts (modules, controllers, services).
- Access to a terminal or command prompt.
Expected Outcome: A functional NestJS application with a /mms/send
endpoint that successfully sends MMS messages via Plivo when provided with valid recipient numbers, text, and publicly accessible media URLs.
1. Setting up the Project
Let's initialize a new NestJS project and install necessary dependencies.
-
Create a new NestJS project: Open your terminal and run the NestJS CLI command:
nest new plivo-mms-sender cd plivo-mms-sender
-
Install Dependencies: We need the Plivo SDK, configuration management, and validation libraries.
npm install plivo @nestjs/config class-validator class-transformer dotenv # or using yarn: # yarn add plivo @nestjs/config class-validator class-transformer dotenv
-
Project Structure: NestJS CLI creates a standard structure. We will primarily work within the
src
directory, creating modules, controllers, and services. -
Environment Variable Setup: Create a
.env
file in the project root for storing sensitive credentials:#.env # Plivo Credentials PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN PLIVO_SENDER_NUMBER=YOUR_PLIVO_MMS_ENABLED_NUMBER # e.g., +14155551212
- Replace the placeholder values with your actual credentials obtained from the Plivo console (see Section 4).
- Crucially, add
.env
to your.gitignore
file to prevent committing secrets.
-
Configure ConfigModule: Import and configure
@nestjs/config
in your main application module (src/app.module.ts
) to load variables from the.env
file.// src/app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { ConfigModule } from '@nestjs/config'; // Import ConfigModule import { MmsModule } from './mms/mms.module'; // We will create this next @Module({ imports: [ ConfigModule.forRoot({ // Configure it isGlobal: true, // Make config available globally envFilePath: '.env', // Specify the env file path }), MmsModule, // Import the module we will create ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
isGlobal: true
makes theConfigService
available throughout the application without needing to importConfigModule
everywhere.envFilePath: '.env'
tells the module where to find our environment variables file.
2. Implementing Core Functionality (Plivo Service)
We'll create a dedicated module and service to handle Plivo interactions, keeping the logic separated and reusable.
-
Generate the Module and Service: Use the Nest CLI to generate the necessary files:
nest generate module mms nest generate service mms/plivo --flat # Use --flat to avoid creating a subdirectory
This creates
src/mms/mms.module.ts
andsrc/mms/plivo.service.ts
. TheMmsModule
was already imported intoAppModule
in the previous step. -
Implement PlivoService: Open
src/mms/plivo.service.ts
and implement the logic to send MMS messages.// src/mms/plivo.service.ts import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as plivo from 'plivo'; @Injectable() export class PlivoService { private readonly logger = new Logger(PlivoService.name); private readonly plivoClient: plivo.Client; private readonly senderNumber: string; constructor(private readonly configService: ConfigService) { const authId = this.configService.get<string>('PLIVO_AUTH_ID'); const authToken = this.configService.get<string>('PLIVO_AUTH_TOKEN'); this.senderNumber = this.configService.get<string>('PLIVO_SENDER_NUMBER'); if (!authId || !authToken || !this.senderNumber) { this.logger.error('Plivo credentials or sender number missing in environment variables.'); throw new InternalServerErrorException('Plivo configuration is incomplete.'); } // Initialize the Plivo client this.plivoClient = new plivo.Client(authId, authToken); this.logger.log('Plivo Client Initialized'); } /** * Sends an MMS message using Plivo. * @param recipient - The destination phone number (E.164 format, e.g., +14155552671). * @param message - The text part of the message. * @param mediaUrls - An array of publicly accessible URLs for the media files. * @returns The response from the Plivo API. * @throws InternalServerErrorException if sending fails. */ async sendMms(recipient: string, message: string, mediaUrls: string[]): Promise<any> { this.logger.log(`Attempting to send MMS to ${recipient}`); if (!mediaUrls || mediaUrls.length === 0) { this.logger.warn(`Attempted to send MMS to ${recipient} with no media URLs. Sending as SMS instead.`); // Optionally handle this case by sending an SMS or throwing an error // For this example, we'll proceed, but Plivo might reject or send as SMS // return this.sendSms(recipient, message); // Example fallback } try { const response = await this.plivoClient.messages.create({ src: this.senderNumber, dst: recipient, text: message, type: 'mms', // Specify message type as MMS media_urls: mediaUrls, // Attach media URLs // You can also use media_ids if you upload media to Plivo first: // media_ids: ['your_uploaded_media_id_1', 'your_uploaded_media_id_2'] }); this.logger.log(`MMS queued successfully to ${recipient}. Message UUID: ${response.messageUuid}`); this.logger.debug(`Plivo API Response: ${JSON.stringify(response)}`); return response; } catch (error) { this.logger.error(`Failed to send MMS to ${recipient}: ${error.message}`, error.stack); // Consider more specific error handling based on Plivo error codes if needed throw new InternalServerErrorException(`Failed to send MMS via Plivo: ${error.message}`); } } // Optional: Add an SMS sending method if needed as a fallback or separate feature async sendSms(recipient: string, message: string): Promise<any> { // Implementation similar to sendMms but with type: 'sms' and no media_urls // ... } }
- Constructor: Injects
ConfigService
to retrieve credentials from environment variables. Initializes theplivo.Client
. Includes checks to ensure configuration is present. sendMms
Method: Takes recipient, message, and media URLs. Constructs the payload forplivoClient.messages.create
. Specifiestype: 'mms'
and passesmedia_urls
. Logs success and errors. Wraps the Plivo call in atry...catch
block to handle potential API errors. Throws a standard NestJS exception on failure.- Alternative (
media_ids
): The comment highlights that you can upload media to Plivo first (using their console or API) and then reference themedia_ids
instead ofmedia_urls
. This is useful if your media isn't publicly hosted.
- Constructor: Injects
-
Export PlivoService: Make sure
PlivoService
is listed in theproviders
andexports
arrays insrc/mms/mms.module.ts
so it can be injected into other modules (like our future controller).// src/mms/mms.module.ts import { Module } from '@nestjs/common'; import { PlivoService } from './plivo.service'; // No controller needed in this specific module if API is handled elsewhere @Module({ providers: [PlivoService], exports: [PlivoService], // Export the service }) export class MmsModule {}
3. Building a Complete API Layer
Now, let's create the API endpoint that external clients can call.
-
Generate the Controller: We'll place the controller within the
mms
module structure.nest generate controller mms --flat
This creates
src/mms/mms.controller.ts
and adds it tosrc/mms/mms.module.ts
. -
Create a Data Transfer Object (DTO): Define the expected shape of the incoming request body using a DTO class with validation decorators. Create a new directory
src/mms/dto
and a filesrc/mms/dto/send-mms.dto.ts
.// src/mms/dto/send-mms.dto.ts import { IsNotEmpty, IsString, IsPhoneNumber, IsArray, IsUrl, ArrayMinSize, ArrayMaxSize } from 'class-validator'; export class SendMmsDto { @IsNotEmpty() @IsPhoneNumber(null) // Use null for generic E.164 format validation recipient: string; @IsNotEmpty() @IsString() message: string; @IsArray() @ArrayMinSize(1) // Must have at least one media URL @ArrayMaxSize(10) // Plivo limit (check current docs for specifics) @IsUrl({}, { each: true }) // Validate each item in the array as a URL mediaUrls: string[]; }
- Uses
class-validator
decorators to enforce rules:recipient
must be a valid phone number (E.164 format like+1...
),message
must be a non-empty string, andmediaUrls
must be an array of valid URLs (at least 1, max 10).
- Uses
-
Implement the Controller: Open
src/mms/mms.controller.ts
and define the endpoint.// src/mms/mms.controller.ts import { Controller, Post, Body, UsePipes, ValidationPipe, Logger, HttpCode, HttpStatus } from '@nestjs/common'; import { PlivoService } from './plivo.service'; import { SendMmsDto } from './dto/send-mms.dto'; // Corrected import path @Controller('mms') // Route prefix: /mms export class MmsController { private readonly logger = new Logger(MmsController.name); constructor(private readonly plivoService: PlivoService) {} @Post('send') // Endpoint: POST /mms/send @HttpCode(HttpStatus.ACCEPTED) // Return 202 Accepted on success (MMS is queued) @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) // Enable validation async sendMms(@Body() sendMmsDto: SendMmsDto) { this.logger.log(`Received request to send MMS to ${sendMmsDto.recipient}`); // The PlivoService handles errors internally and throws exceptions const result = await this.plivoService.sendMms( sendMmsDto.recipient, sendMmsDto.message, sendMmsDto.mediaUrls, ); // Return a structured response return { message: 'MMS successfully queued for delivery.', recipient: sendMmsDto.recipient, messageUuid: result.messageUuid ? result.messageUuid[0] : null, // Extract UUID apiId: result.apiId, }; } }
@Controller('mms')
: Sets the base route for this controller to/mms
.@Post('send')
: Defines a POST endpoint at/mms/send
.constructor
: Injects ourPlivoService
.@Body() sendMmsDto: SendMmsDto
: Binds the request body to our DTO.@UsePipes(new ValidationPipe(...))
: Automatically validates the incomingSendMmsDto
based on the decorators we added.whitelist: true
strips any properties not defined in the DTO.transform: true
enables automatic type conversion (e.g., string to number if needed, though not here).@HttpCode(HttpStatus.ACCEPTED)
: Sets the default success status code to 202, indicating the request was accepted and is being processed (MMS sending is asynchronous).- The method calls
plivoService.sendMms
and returns a structured success response including the Plivo Message UUID.
-
Enable Validation Globally (Optional but Recommended): Instead of
@UsePipes
on every handler, you can enable theValidationPipe
globally insrc/main.ts
.// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; // Import ValidationPipe import { Logger } from '@nestjs/common'; // Import Logger async function bootstrap() { const app = await NestFactory.create(AppModule); const logger = new Logger('Bootstrap'); // Enable global validation pipe app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, // Optional: Throw error if extra fields are present })); const port = process.env.PORT || 3000; await app.listen(port); logger.log(`Application listening on port ${port}`); } bootstrap();
-
Testing with cURL/Postman:
- Start the application:
npm run start:dev
- Send a POST request:
cURL Example:
curl -X POST http://localhost:3000/mms/send \ -H "Content-Type: application/json" \ -d '{ "recipient": "+1RECIPIENTNUMBER", "message": "Check out this cool GIF from NestJS!", "mediaUrls": ["https://media.giphy.com/media/26gscSULUcfKU7dHq/source.gif"] }'
Replace
+1RECIPIENTNUMBER
with a valid test phone number (must be verified if using a Plivo trial account).Expected JSON Request Body:
{ "recipient": "+14155552671", "message": "Hello from NestJS with Media!", "mediaUrls": ["https://www.plivo.com/static/v2/img/logo.png"] }
Expected JSON Response (Success - Status 202 Accepted):
{ "message": "MMS successfully queued for delivery.", "recipient": "+14155552671", "messageUuid": "d374b3f4-b17e-11ec-b4e1-0242ac110005", "apiId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
Expected JSON Response (Validation Error - Status 400 Bad Request):
{ "statusCode": 400, "message": [ "recipient must be a valid phone number", "mediaUrls should not be empty" ], "error": "Bad Request" }
- Start the application:
4. Integrating with Plivo (Configuration Details)
Proper configuration is key to connecting with Plivo.
-
Obtain Plivo Credentials:
- Log in to your Plivo Console: https://console.plivo.com/
- Navigate to the Account section, usually found at the top right or main dashboard overview.
- You will find your Auth ID and Auth Token here.
- Copy these values into your
.env
file forPLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
.
-
Obtain an MMS-Enabled Plivo Number:
- In the Plivo Console, navigate to Phone Numbers -> Buy Numbers.
- Filter by country (US or Canada for MMS) and ensure the MMS capability checkbox is selected.
- Search for and purchase a suitable number.
- Once purchased, find the number under Phone Numbers -> Your Numbers.
- Copy the full number in E.164 format (e.g.,
+14155551212
) into your.env
file forPLIVO_SENDER_NUMBER
.
-
Environment Variable Details:
PLIVO_AUTH_ID
- Purpose: Your Plivo account identifier for API authentication.
- Format: A string of letters and numbers (e.g.,
MANJMWNZYXEXAMPLE
). - How to Obtain: Plivo Console -> Account Overview.
PLIVO_AUTH_TOKEN
- Purpose: Your Plivo account secret key for API authentication. Treat like a password.
- Format: A string of letters, numbers, and potentially symbols (e.g.,
Nzc0ZmE4ZmFlODE3YmY5ZWE4YjY2NjA2YmExample
). - How to Obtain: Plivo Console -> Account Overview.
PLIVO_SENDER_NUMBER
- Purpose: The Plivo phone number (must be MMS-enabled) that will appear as the sender of the MMS.
- Format: E.164 format string (e.g.,
+14155551212
). - How to Obtain: Plivo Console -> Phone Numbers -> Your Numbers (after purchasing).
-
Secure Handling:
- Never hardcode credentials directly in your source code.
- Use environment variables (
.env
for local development, platform-specific secrets management for production). - Ensure
.env
is included in your.gitignore
file.
5. Error Handling, Logging, and Retry Mechanisms
Robust applications need proper error handling and logging.
-
Error Handling Strategy:
- Validation Errors: Handled by
ValidationPipe
(returns 400 Bad Request). - Plivo API Errors: Caught within
PlivoService
. Logs detailed error information and throwsInternalServerErrorException
(returns 500 Internal Server Error). This prevents leaking sensitive Plivo error details directly to the client but provides internal visibility. - Configuration Errors: Checked in
PlivoService
constructor; throwsInternalServerErrorException
on startup if config is missing. - Custom Exception Filter (Advanced): For more granular control, you could create a custom NestJS exception filter to map specific Plivo errors (e.g., invalid recipient number
400
) to appropriate HTTP status codes.
- Validation Errors: Handled by
-
Logging:
- We are using NestJS's built-in
Logger
. PlivoService
: Logs initialization, attempts to send, success (with Message UUID), and errors (with stack trace).MmsController
: Logs incoming requests.- Log Levels: Use different levels (
log
,debug
,warn
,error
) appropriately.debug
can be used for verbose information like the full Plivo API response. - Production Logging: Consider using a more structured logger like
pino
(e.g., withnestjs-pino
) to output JSON logs, which are easier for log aggregation systems (like Datadog, Splunk, ELK) to parse.
- We are using NestJS's built-in
-
Retry Mechanisms: MMS sending can occasionally fail due to transient network issues or temporary Plivo/carrier problems. Implementing retries can improve reliability.
- Simple Retry: You could add a simple loop within the
catch
block ofPlivoService.sendMms
for 1-2 retries with a short delay. - Exponential Backoff: A better approach uses increasing delays between retries. Libraries like
async-retry
can simplify this.
Conceptual Example using
async-retry
(Installnpm i async-retry @types/async-retry
):// src/mms/plivo.service.ts (Illustrative Snippet) import * as retry from 'async-retry'; // ... other imports async sendMms(recipient: string, message: string, mediaUrls: string[]): Promise<any> { this.logger.log(`Attempting to send MMS to ${recipient}`); // ... validation ... try { // Wrap the Plivo call with retry logic const response = await retry( async (bail) => { // 'bail' function allows exiting retry early for non-retryable errors try { const apiResponse = await this.plivoClient.messages.create({ src: this.senderNumber, dst: recipient, text: message, type: 'mms', media_urls: mediaUrls, }); this.logger.log(`Plivo API call successful for ${recipient}.`); return apiResponse; } catch (error) { this.logger.warn(`Plivo API call failed for ${recipient}. Retrying... Error: ${error.message}`); // Example: Don't retry on specific Plivo errors like invalid number (400) if (error.statusCode === 400) { this.logger.error(`Non-retryable error sending MMS to ${recipient}: ${error.message}`); bail(error); // Stop retrying return; // Required after bail } throw error; // Throw error to trigger retry for other issues } }, { retries: 3, // Number of retries factor: 2, // Exponential backoff factor minTimeout: 1000, // Initial delay 1 second maxTimeout: 5000, // Max delay 5 seconds onRetry: (error, attempt) => { this.logger.warn(`Retrying MMS to ${recipient} (Attempt ${attempt}/${3}): ${error.message}`); }, }, ); this.logger.log(`MMS queued successfully to ${recipient} after retries. Message UUID: ${response.messageUuid}`); // ... rest of success logic ... return response; } catch (error) { this.logger.error(`Failed to send MMS to ${recipient} after all retries: ${error.message}`, error.stack); throw new InternalServerErrorException(`Failed to send MMS via Plivo after retries: ${error.message}`); } }
- This example attempts the Plivo call up to 4 times (initial + 3 retries) with increasing delays. It also shows how to
bail
out of retries for specific error conditions.
- Simple Retry: You could add a simple loop within the
-
Testing Error Scenarios:
- Provide invalid credentials in
.env
-> Expect 500 error on startup or first API call. - Send request with invalid recipient format -> Expect 400 Bad Request from ValidationPipe.
- Send request with non-existent media URL -> Expect 500 Internal Server Error from PlivoService (Plivo API will likely reject).
- Temporarily disable network connectivity -> Test retry mechanism (if implemented).
- Provide invalid credentials in
6. Creating a Database Schema and Data Layer
For this specific guide focused only on sending MMS, a database is not strictly required. However, in a real-world application, you'd likely want to persist information about sent messages.
If you needed persistence:
-
Schema: You might create a
messages
table.id
(Primary Key, e.g., UUID)recipient
(String)sender
(String, Your Plivo Number)body
(Text)media_urls
(JSON or Text Array)plivo_message_uuid
(String, Index) - To correlate with Plivo logs/webhooksstatus
(Enum: 'queued', 'sent', 'failed', 'delivered') - Updated via webhookscreated_at
(Timestamp)updated_at
(Timestamp)
-
Entity Relationship Diagram (ERD): (Simple: Just one table initially)
+---------------------+ | Messages | +---------------------+ | PK id | | recipient | | sender | | body | | media_urls | | plivo_message_uuid | | status | | created_at | | updated_at | +---------------------+
-
Implementation:
- Use an ORM like TypeORM or Prisma.
- Create a
Message
entity/model. - Inject the repository/client into
PlivoService
or a dedicatedMessageService
. - Save a record before calling the Plivo API (status 'queued').
- Update the record with the
plivo_message_uuid
upon successful queuing. - Implement a separate webhook endpoint to receive status updates from Plivo and update the message status accordingly (e.g., 'sent', 'failed', 'delivered'). This is outside the scope of the sending guide.
-
Migrations: Use the ORM's migration tools (
typeorm migration:run
,prisma migrate dev
) to manage schema changes.
(Database implementation details are omitted here to maintain focus on MMS sending.)
7. Adding Security Features
Security is paramount, especially when dealing with APIs and external services.
-
Input Validation and Sanitization:
- Done: We are using
class-validator
viaValidationPipe
in theMmsController
. This ensures data conforms to expected types and formats (e.g., phone numbers, URLs). whitelist: true
prevents processing of unexpected fields.- NestJS generally protects against common injection attacks if standard practices (like using ORMs correctly, not constructing raw SQL queries with user input) are followed.
- Done: We are using
-
API Key Security:
- Done: Plivo Auth ID and Token are stored securely using environment variables and not exposed in code or to the client.
-
Rate Limiting:
- Protect your API endpoint from abuse (accidental or malicious) by limiting the number of requests a client can make in a given time window.
- Use the
@nestjs/throttler
module.
Setup: a. Install:
npm install @nestjs/throttler
b. Import and configure insrc/app.module.ts
:// src/app.module.ts import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core'; // ... other imports @Module({ imports: [ // ... ConfigModule, MmsModule ThrottlerModule.forRoot([{ ttl: 60000, // Time window in milliseconds (e.g., 60 seconds) limit: 10, // Max requests per IP in the window }]), ], controllers: [AppController], providers: [ AppService, // Apply ThrottlerGuard globally { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {}
c. Now, by default, every endpoint will be limited to 10 requests per minute per IP address. You can customize this per-controller or per-route using decorators (
@SkipThrottle()
,@Throttle()
). -
Protecting the API Endpoint (If Public):
- If this NestJS API is publicly accessible, consider adding an authentication/authorization layer (e.g., API Key check, JWT) using NestJS Guards to ensure only authorized clients can trigger MMS sends. This is separate from Plivo's authentication.
-
Common Vulnerabilities:
- Ensure dependencies are kept up-to-date (
npm audit
) to patch known vulnerabilities. - Be cautious if handling user-uploaded files directly; ensure proper validation and storage. (Here, we only handle URLs).
- Ensure dependencies are kept up-to-date (
-
Testing for Security:
- Attempt to send requests with invalid or malicious inputs (e.g., script tags in messages, malformed JSON). Validation should block these.
- Test rate limiting by sending rapid requests. Expect
429 Too Many Requests
errors after hitting the limit. - Use security scanning tools if deploying to production.
8. Handling Special Cases Relevant to the Domain
MMS messaging has nuances to consider.
-
Media URL Accessibility:
- Crucial: The URLs provided in
mediaUrls
must be publicly accessible over the internet. Plivo's servers need to fetch the media from these URLs. Private network URLs or localhost URLs will not work. - Ensure URLs point directly to the media file (e.g.,
https://domain.com/image.jpg
) and not to an HTML page containing the media.
- Crucial: The URLs provided in
-
Media Types and Size Limits:
- Plivo supports common image types (JPEG, PNG, GIF) and potentially others (refer to current Plivo documentation for a definitive list).
- There are size limits per file and potentially per message total. Plivo's documentation (and research) often cites around 2MB per file and a maximum of 10 media attachments per message. Ensure your application respects these limits, either through validation or clear user guidance.
- Handle errors from Plivo related to unsupported media types or sizes gracefully.
-
Character Limits for Text:
- While MMS allows for longer text portions than SMS, carriers might still impose limits (often much higher, e.g., 1000+ characters, but variable). Be aware of potential truncation, although Plivo might handle segmentation. For critical information, keep text reasonably concise.
-
Country Restrictions:
- Plivo's MMS sending capability is primarily available for US and Canadian numbers. Sending MMS to other countries may not be supported or may fall back to SMS. Verify Plivo's current supported countries. Your application logic might need checks if targeting multiple regions.
-
Plivo Trial Account Limitations:
- If using a Plivo trial account, you can typically only send messages (SMS/MMS) to phone numbers that have been verified within your Plivo console (Sandbox Numbers section). Ensure your test recipient numbers are verified.
-
Duplicate Requests:
- Consider idempotency if clients might retry requests. You could potentially check the database (if implemented) for a recent message to the same recipient with similar content, or rely on client-generated idempotency keys if necessary. For simple MMS sending, this is often less critical than for payments.
9. Implementing Performance Optimizations
For a service primarily making external API calls, optimization focuses on efficiency and non-blocking operations.
-
Asynchronous Operations:
- Done: NestJS is built on Node.js, which is asynchronous by nature. All I/O operations, including the HTTP call to Plivo via the SDK, are non-blocking. This ensures the server remains responsive to other requests while waiting for Plivo's API.
-
Efficient Plivo Client Usage:
- Done: We initialize the
plivo.Client
once in thePlivoService
constructor and reuse it for subsequent requests. This avoids the overhead of creating a new client instance for every MMS send. The SDK likely handles underlying HTTP connection pooling.
- Done: We initialize the
-
Payload Size:
- Be mindful of the size of the request/response payloads if logging heavily, but for this use case, it's generally not a major bottleneck unless logging full media file contents (which we are not).
-
Caching (Limited Applicability):
- Caching is less relevant for the act of sending a unique MMS. However, if you frequently retrieve related data (e.g., user profiles before sending), caching that data could improve overall request latency.
-
Load Testing:
- Use tools like
k6
,Artillery
, orJMeter
to simulate concurrent users sending MMS requests to your endpoint. - Monitor resource usage (CPU, memory) of your NestJS application and response times.
- Identify bottlenecks – is it your application, the Plivo API latency, or network constraints? Adjust resources or optimize code as needed.
- Use tools like