This guide provides a step-by-step walkthrough for building a production-ready NestJS application capable of sending SMS messages using the MessageBird API and its Node.js SDK. We will cover project setup, core implementation, configuration, error handling, security, testing, and deployment considerations.
By the end of this guide, you will have a robust API endpoint that accepts a recipient phone number and a message body, validates the input, and uses MessageBird to reliably deliver the SMS. This solves the common need for programmatic SMS notifications, alerts, or basic communication features within a modern web application.
Project Overview and Goals
-
Goal: Create a simple NestJS API endpoint (
POST /sms/send
) to send outbound SMS messages via MessageBird. -
Problem Solved: Enables applications to programmatically send SMS messages for various purposes like notifications, verification codes (though MessageBird's Verify API is often better suited for OTP), or simple alerts.
-
Technologies:
- Node.js: The underlying runtime environment.
- NestJS: A progressive Node.js framework for building efficient, reliable, and scalable server-side applications. Chosen for its robust structure, dependency injection, modularity, and built-in support for features like validation and configuration management.
- MessageBird Node.js SDK: Simplifies interaction with the MessageBird REST API.
- dotenv / @nestjs/config: For managing environment variables securely.
- class-validator / class-transformer: For robust request payload validation.
- libphonenumber-js (Optional): For stricter phone number validation.
-
Architecture:
Client (e.g., Postman, Frontend App) | | HTTP POST Request (/sms/send) V NestJS Application | - Controller (Handles request, validation using DTO) | - Service (Contains business logic, interacts with MessageBird SDK) | - Module (Organizes components) | - ConfigModule (Loads environment variables) V MessageBird SDK | | API Call (messages.create) V MessageBird API | | SMS Delivery Network V Recipient's Phone
-
Prerequisites:
- Node.js (LTS version recommended) and npm/yarn installed.
- A MessageBird account (Free trial available).
- A text editor or IDE (e.g., VS Code).
- Basic understanding of TypeScript, Node.js, and REST APIs.
- A purchased virtual number or registered Alphanumeric Sender ID in your MessageBird account to use as the
originator
.
1. Setting up the Project
Let's initialize our NestJS project and install the necessary dependencies.
-
Install NestJS CLI: If you don't have it, install the NestJS command-line interface globally.
npm install -g @nestjs/cli
-
Create New NestJS Project: Generate a new project using the CLI.
nest new nestjs-messagebird-sms
Choose your preferred package manager (npm or yarn) when prompted.
-
Navigate to Project Directory:
cd nestjs-messagebird-sms
-
Install Dependencies: We need the MessageBird SDK, and packages for configuration management and validation.
# Using npm npm install messagebird @nestjs/config dotenv class-validator class-transformer # Using yarn yarn add messagebird @nestjs/config dotenv class-validator class-transformer
messagebird
: The official Node.js SDK for the MessageBird API.@nestjs/config
: Handles environment variables gracefully within NestJS.dotenv
: Loads environment variables from a.env
file intoprocess.env
. Used by@nestjs/config
.class-validator
&class-transformer
: Enable easy implementation of validation rules using decorators on DTOs (Data Transfer Objects).
-
Project Structure: The Nest CLI sets up a standard structure (
src/
containsapp.module.ts
,app.controller.ts
,app.service.ts
,main.ts
). We will create dedicated modules, services, and controllers for SMS functionality.
2. Configuring Environment Variables
Sensitive information like API keys should never be hardcoded. We'll use environment variables.
-
Get MessageBird API Key:
- Log in to your MessageBird Dashboard.
- Navigate to the
Developers
section in the left-hand menu. - Click on
API access
. - You can use your live API key or switch to test mode to get a test key. For initial development, the test key is sufficient and won't incur charges or send actual SMS. Copy the appropriate key.
- Important: Keep your live API key secure and confidential.
-
Get MessageBird Originator:
- This is the sender ID displayed on the recipient's phone. It can be:
- A virtual mobile number purchased from MessageBird (e.g.,
+12025550134
). Ensure it's SMS-enabled. - An Alphanumeric Sender ID (e.g.,
MyCompany
) registered and approved in your MessageBird account (availability and regulations vary by country).
- A virtual mobile number purchased from MessageBird (e.g.,
- Find or purchase/register this in the ""Numbers"" section of the MessageBird Dashboard.
- This is the sender ID displayed on the recipient's phone. It can be:
-
Create
.env
File: In the root directory of your project, create a file named.env
.touch .env
-
Add Variables to
.env
: Add your MessageBird API key and originator to the file. Replace placeholders with your actual values.# .env # MessageBird Configuration MESSAGEBIRD_API_KEY=YOUR_API_KEY # Use 'test_...' or 'live_...' key MESSAGEBIRD_ORIGINATOR=YOUR_SENDER_ID # e.g., +12025550134 or MyCompany
-
Ignore
.env
File: Add.env
to your.gitignore
file to prevent accidentally committing secrets.# .gitignore # ... other entries .env
-
Configure NestJS ConfigModule: Import and configure
ConfigModule
in your main application module (src/app.module.ts
) to make environment variables accessible throughout the application via dependency injection.// src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; // Import ConfigModule import { AppController } from './app.controller'; import { AppService } from './app.service'; import { SmsModule } from './sms/sms.module'; // We will create this next @Module({ imports: [ ConfigModule.forRoot({ // Configure ConfigModule isGlobal: true, // Make ConfigService available globally envFilePath: '.env', // Specify the env file path }), SmsModule, // Import our SMS module ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
isGlobal: true
makes theConfigService
available in any module without needing to importConfigModule
everywhere.envFilePath: '.env'
tells the module where to find the environment file.
3. Implementing Core SMS Functionality (Service)
We'll create a dedicated module and service to handle SMS logic.
-
Generate SMS Module and Service: Use the Nest CLI.
nest generate module sms nest generate service sms
This creates
src/sms/sms.module.ts
andsrc/sms/sms.service.ts
(along with a test file). -
Implement SmsService: Open
src/sms/sms.service.ts
and add the logic to interact with the MessageBird SDK.// src/sms/sms.service.ts import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as MessageBird from 'messagebird'; // Import MessageBird SDK types if available, or use as namespace @Injectable() export class SmsService { private readonly logger = new Logger(SmsService.name); private messagebird: MessageBird.MessageBird; // Declare messagebird client instance using SDK types private originator: string; constructor(private configService: ConfigService) { // Retrieve API key and originator from environment variables const apiKey = this.configService.get<string>('MESSAGEBIRD_API_KEY'); this.originator = this.configService.get<string>('MESSAGEBIRD_ORIGINATOR'); if (!apiKey || !this.originator) { this.logger.error('MessageBird API Key or Originator not configured in .env file'); throw new Error('MessageBird environment variables missing.'); } // Initialize the MessageBird client // The require('messagebird')(key) pattern is common for initializing CJS modules like this SDK. // Using 'any' here if specific types for the initializer function aren't readily available or inferred. try { const mbInitializer: any = require('messagebird'); this.messagebird = mbInitializer(apiKey); this.logger.log('MessageBird SDK initialized successfully.'); } catch (error) { this.logger.error('Failed to initialize MessageBird SDK', error); throw error; // Re-throw to prevent service usage if initialization fails } } /** * Sends an SMS message using the MessageBird API. * @param recipient - The recipient's phone number (E.164 format recommended). * @param body - The text content of the SMS message. * @returns Promise resolving with the MessageBird API response or rejecting with an error. */ async sendSms(recipient: string, body: string): Promise<any> { // Consider defining a stricter return type based on expected MessageBird response const params: MessageBird.MessageParameters = { originator: this.originator, recipients: [recipient], // Expects an array of recipients body: body, }; this.logger.log(`Attempting to send SMS to ${recipient} from ${this.originator}`); // The MessageBird SDK's messages.create uses a callback. // We wrap it in a Promise for modern async/await usage. return new Promise((resolve, reject) => { this.messagebird.messages.create(params, (err, response) => { if (err) { this.logger.error(`Failed to send SMS to ${recipient}`, err); // Provide more context if available in the error object const errorDetails = err.errors ? JSON.stringify(err.errors) : err.message; reject(new Error(`MessageBird API Error: ${errorDetails}`)); } else { this.logger.log(`SMS sent successfully to ${recipient}. Message ID: ${response.id}`); // The response contains details about the sent message(s) resolve(response); } }); }); } }
- Dependencies: We inject
ConfigService
to access environment variables.Logger
provides built-in NestJS logging. - Initialization: The
constructor
retrieves the API key and originator, then initializes themessagebird
client. Robust error handling is added for missing variables or initialization failure. The comment clarifies the use ofrequire
andany
for CJS interop. sendSms
Method:- Takes
recipient
andbody
as arguments. - Constructs the
params
object required bymessagebird.messages.create
, using the configuredoriginator
. - Wraps the callback-based SDK call in a
Promise
for compatibility with NestJS's async/await patterns. - Logs success or failure using the
Logger
. - Resolves with the MessageBird response on success, rejects with a formatted error on failure.
- Takes
- Dependencies: We inject
-
Update SmsModule: Ensure
SmsService
is listed as a provider insrc/sms/sms.module.ts
. Nest CLI usually does this automatically.// src/sms/sms.module.ts import { Module } from '@nestjs/common'; import { SmsService } from './sms.service'; // If we add a controller later, import it here import { SmsController } from './sms.controller'; // Import controller @Module({ controllers: [SmsController], // Add controller when created providers: [SmsService], exports: [SmsService], // Export service if needed by other modules }) export class SmsModule {}
4. Building the API Layer (Controller and DTO)
Now, let's create the API endpoint to trigger the SmsService
.
-
Generate SMS Controller:
nest generate controller sms
This creates
src/sms/sms.controller.ts
. -
Create Request DTO: Create a Data Transfer Object (DTO) to define the expected shape and validation rules for the request body. Create a file
src/sms/dto/send-sms.dto.ts
.// src/sms/dto/send-sms.dto.ts import { IsNotEmpty, IsString, MinLength, IsPhoneNumber } from 'class-validator'; export class SendSmsDto { @IsNotEmpty({ message: 'Recipient phone number is required.' }) // Use IsPhoneNumber for stricter E.164 validation. // Requires installing libphonenumber-js: run 'npm install libphonenumber-js' or 'yarn add libphonenumber-js' // @IsPhoneNumber(null, { message: 'Recipient must be a valid phone number (E.164 format recommended).' }) @IsString() // Use IsString for a basic check if libphonenumber-js is not installed readonly recipient: string; @IsNotEmpty({ message: 'Message body cannot be empty.' }) @IsString() @MinLength(1, { message: 'Message body must contain at least 1 character.' }) readonly body: string; }
- We use decorators from
class-validator
(@IsNotEmpty
,@IsString
,@MinLength
, optionally@IsPhoneNumber
) to define validation rules. - Clear instructions are added for installing
libphonenumber-js
if@IsPhoneNumber
is used.
- We use decorators from
-
Implement SmsController: Open
src/sms/sms.controller.ts
and define the endpoint.// src/sms/sms.controller.ts import { Controller, Post, Body, UsePipes, ValidationPipe, Logger, HttpException, HttpStatus } from '@nestjs/common'; import { SmsService } from './sms.service'; import { SendSmsDto } from './dto/send-sms.dto'; @Controller('sms') // Route prefix for this controller export class SmsController { private readonly logger = new Logger(SmsController.name); constructor(private readonly smsService: SmsService) {} @Post('send') // Route: POST /sms/send @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) // Enable validation using the DTO async sendSms(@Body() sendSmsDto: SendSmsDto) { this.logger.log(`Received request to send SMS: ${JSON.stringify(sendSmsDto)}`); try { const result = await this.smsService.sendSms(sendSmsDto.recipient, sendSmsDto.body); // Successfully sent (or at least accepted by MessageBird) return { message: 'SMS submitted successfully.', data: { messageId: result.id, // Or relevant data from the MessageBird response recipients: result.recipients.totalSentCount, }, }; } catch (error) { this.logger.error(`Error sending SMS: ${error.message}`, error.stack); // Throw standard HTTP exceptions throw new HttpException( `Failed to send SMS: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR, // Or BAD_GATEWAY if it's clearly an external API issue ); } } }
@Controller('sms')
: Sets the base path for all routes in this controller to/sms
.@Post('send')
: Defines a handler forPOST
requests to/sms/send
.@Body() sendSmsDto: SendSmsDto
: Injects the validated request body into thesendSmsDto
parameter.@UsePipes(new ValidationPipe(...))
: Automatically validates the incoming body against theSendSmsDto
.transform: true
: Automatically transforms the incoming plain object to an instance ofSendSmsDto
.whitelist: true
: Strips any properties from the request body that are not defined in the DTO.
- Error Handling: The
try...catch
block handles potential errors from theSmsService
and throws anHttpException
with an appropriate status code and message.
-
Update SmsModule: Add the
SmsController
to thecontrollers
array insrc/sms/sms.module.ts
.// src/sms/sms.module.ts import { Module } from '@nestjs/common'; import { SmsService } from './sms.service'; import { SmsController } from './sms.controller'; // Import the controller @Module({ controllers: [SmsController], // Add the controller providers: [SmsService], exports: [SmsService], }) export class SmsModule {}
-
Enable Validation Globally (Optional but Recommended): Instead of applying
@UsePipes
to every controller method, you can enable theValidationPipe
globally insrc/main.ts
.// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Logger, ValidationPipe } from '@nestjs/common'; // Import ValidationPipe async function bootstrap() { const app = await NestFactory.create(AppModule); const logger = new Logger('Bootstrap'); // Create a logger instance // Enable global validation pipe app.useGlobalPipes(new ValidationPipe({ transform: true, // Automatically transform payloads to DTO instances whitelist: true, // Strip properties not defined in DTO forbidNonWhitelisted: true, // Throw error if non-whitelisted properties are present transformOptions: { enableImplicitConversion: true, // Allow basic type conversions }, })); // Configure global prefix (optional) // app.setGlobalPrefix('api/v1'); const port = process.env.PORT || 3000; await app.listen(port); logger.log(`Application listening on port ${port}`); // Removed log related to /api documentation as Swagger setup is not included. } bootstrap();
If you enable it globally, you can remove the
@UsePipes(...)
decorator from theSmsController
.
5. Testing the API Endpoint
Let's verify our implementation.
-
Start the Application:
npm run start:dev
This starts the NestJS application in watch mode. Look for the log message indicating the port it's running on (usually 3000).
-
Send a Test Request (Using
curl
): Open a new terminal window and usecurl
(or a tool like Postman or Insomnia) to send a POST request. ReplaceYOUR_RECIPIENT_NUMBER
with a valid phone number (use your own for testing with a live key, or any validly formatted number for a test key).curl -X POST http://localhost:3000/sms/send \ -H ""Content-Type: application/json"" \ -d '{ ""recipient"": ""YOUR_RECIPIENT_NUMBER"", ""body"": ""Hello from my NestJS MessageBird App!"" }'
-
Check Response:
-
Success: You should receive a JSON response similar to:
{ ""message"": ""SMS submitted successfully."", ""data"": { ""messageId"": ""some-messagebird-message-id"", ""recipients"": 1 } }
Check your application console logs for success messages. If using a live key, check the recipient phone for the SMS. Check the MessageBird Dashboard logs (Logs -> Messages) to see the status.
-
Validation Error: If you send invalid data (e.g., missing
body
), you'll get a 400 Bad Request response detailing the errors:{ ""statusCode"": 400, ""message"": [ ""Message body cannot be empty."", ""Message body must contain at least 1 character."" ], ""error"": ""Bad Request"" }
-
API Error: If MessageBird rejects the request (e.g., invalid API key, insufficient balance, invalid originator), you'll get a 500 Internal Server Error (or the status code you configured in the controller's
HttpException
):{ ""statusCode"": 500, ""message"": ""Failed to send SMS: MessageBird API Error: Authentication failed"" }
Check your application console logs for detailed error messages from the
SmsService
.
-
6. Error Handling and Logging
We've already incorporated basic logging and error handling:
- NestJS Logger: Used in the service and controller to log informational messages and errors. Consider configuring log levels (e.g., only log errors in production) via environment variables or configuration files. You can also integrate more advanced logging libraries like Winston or Pino with NestJS.
- ValidationPipe: Handles input validation errors, returning 400 responses.
- try...catch + HttpException: Catches errors from the
SmsService
(including MessageBird API errors) and translates them into appropriate HTTP responses (e.g., 500 Internal Server Error, 502 Bad Gateway). - MessageBird Error Structure: When the SDK rejects the promise, the
err
object often contains anerrors
array with more specific details from the MessageBird API (code, description, parameter). LoggingJSON.stringify(err.errors)
can be very helpful for debugging.
Retry Mechanisms (Advanced): For critical SMS, you might implement retries with exponential backoff, especially for transient network errors or temporary MessageBird API issues (e.g., rate limiting). Libraries like async-retry
or NestJS scheduler features (@nestjs/schedule
) could be used, but are beyond the scope of this basic guide. Be cautious not to retry indefinitely or for errors that won't resolve (like invalid credentials).
7. Security Considerations
-
API Key Security:
- Never commit API keys to version control (
.env
in.gitignore
). - Use environment variables managed securely in your deployment environment (e.g., secrets management tools, platform environment variables).
- Consider rotating API keys periodically.
- Never commit API keys to version control (
-
Input Validation: Already implemented using
class-validator
DTOs. This prevents malformed requests and basic injection attempts in the body parameters. Always validate and sanitize any user-provided input. -
Rate Limiting: Protect your API endpoint from abuse. Use
@nestjs/throttler
to limit the number of requests per IP address or user within a specific timeframe.npm install --save @nestjs/throttler
Configure it in
app.module.ts
:// src/app.module.ts import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core'; // Import APP_GUARD import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { SmsModule } from './sms/sms.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env' }), ThrottlerModule.forRoot([{ // Configure rate limiting ttl: 60000, // Time window in milliseconds (e.g., 60 seconds) limit: 10, // Max requests per window per IP }]), SmsModule, ], controllers: [AppController], providers: [ AppService, { // Apply ThrottlerGuard globally provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {}
-
Authentication/Authorization: This guide assumes the API endpoint might be internal or protected by other means (e.g., API Gateway, firewall). For public-facing APIs, implement proper authentication (e.g., JWT, API Keys specific to your API consumers) to ensure only authorized clients can trigger SMS sending.
-
Originator Restrictions: Be aware of country-specific regulations regarding Alphanumeric Sender IDs and ensure your chosen originator is compliant and approved.
8. Unit Testing the Service
NestJS encourages testing. Let's write a basic unit test for SmsService
.
-
Open Test File: Navigate to
src/sms/sms.service.spec.ts
. The CLI generates a basic shell. -
Write Unit Tests: Mock dependencies (
ConfigService
,messagebird
SDK) and test thesendSms
method logic.// src/sms/sms.service.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { SmsService } from './sms.service'; import { ConfigService } from '@nestjs/config'; // Logger is not explicitly needed for these tests, so it's removed from providers // Mock the messagebird library const mockMessageBirdCreate = jest.fn(); const mockMessageBirdClient = { messages: { create: mockMessageBirdCreate, }, }; // Mock the require('messagebird') call jest.mock('messagebird', () => { // This mocks the function returned by require('messagebird') return jest.fn().mockImplementation(() => mockMessageBirdClient); }, { virtual: true }); // Use virtual mock for require describe('SmsService', () => { let service: SmsService; let configService: ConfigService; // Mock ConfigService values const mockConfigService = { get: jest.fn((key: string) => { if (key === 'MESSAGEBIRD_API_KEY') return 'test_api_key'; if (key === 'MESSAGEBIRD_ORIGINATOR') return '+1234567890'; return null; }), }; beforeEach(async () => { // Reset mocks before each test mockMessageBirdCreate.mockClear(); // Also clear mocks on the module factory itself if require was mocked (require('messagebird') as jest.Mock).mockClear(); const module: TestingModule = await Test.createTestingModule({ providers: [ SmsService, { provide: ConfigService, useValue: mockConfigService, // Use the mock implementation }, // Logger removed from providers as it's not directly injected/tested here ], }).compile(); service = module.get<SmsService>(SmsService); configService = module.get<ConfigService>(ConfigService); // Get instance if needed for assertions }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('sendSms', () => { const recipient = '+9876543210'; const body = 'Test message'; const originator = '+1234567890'; // Expected from mockConfigService it('should call messagebird.messages.create with correct parameters and resolve on success', async () => { const mockResponse = { id: 'mock-message-id', recipients: { totalSentCount: 1 } }; // Configure the mock implementation for the SDK call for this test mockMessageBirdCreate.mockImplementation((params, callback) => { callback(null, mockResponse); // Simulate success }); await expect(service.sendSms(recipient, body)).resolves.toEqual(mockResponse); expect(mockMessageBirdCreate).toHaveBeenCalledTimes(1); expect(mockMessageBirdCreate).toHaveBeenCalledWith( { originator: originator, recipients: [recipient], body: body, }, expect.any(Function), // The callback function ); }); it('should reject with an error if messagebird.messages.create fails', async () => { const mockError = { message: ""API Error"", // Corrected quotes errors: [{ code: 2, description: 'Request parameter validation failed', parameter: 'recipients' }] }; // Configure the mock implementation for the SDK call for this test mockMessageBirdCreate.mockImplementation((params, callback) => { callback(mockError, null); // Simulate error }); await expect(service.sendSms(recipient, body)).rejects.toThrow( `MessageBird API Error: ${JSON.stringify(mockError.errors)}` // Check for the formatted error ); expect(mockMessageBirdCreate).toHaveBeenCalledTimes(1); expect(mockMessageBirdCreate).toHaveBeenCalledWith( { originator: originator, recipients: [recipient], body: body, }, expect.any(Function), ); }); it('should throw an error during initialization if API key is missing', async () => { // Override config mock for this specific scenario const missingKeyConfig = { get: jest.fn((key: string) => { if (key === 'MESSAGEBIRD_ORIGINATOR') return '+1234567890'; return null; // Simulate missing API key }) }; // We need to test the module compilation which triggers the constructor await expect(Test.createTestingModule({ providers: [ SmsService, { provide: ConfigService, useValue: missingKeyConfig }, // Logger not needed here either ], }).compile()).rejects.toThrow('MessageBird environment variables missing.'); }); }); });
- Mocking: We use
jest.fn()
andjest.mock
to create mock implementations ofConfigService
and themessagebird
SDK. - Test Cases: We test the successful path, the error path, and the constructor initialization error handling.
- Logger:
Logger
has been removed from the test moduleproviders
for cleanliness, as it wasn't actively used in the tests.
- Mocking: We use
-
Run Tests:
npm run test
Or for a specific file:
npm run test -- src/sms/sms.service.spec.ts
9. Troubleshooting and Caveats
- Error:
Authentication failed
: Double-check yourMESSAGEBIRD_API_KEY
in.env
. Ensure you're using the correct key (live vs. test) and that it hasn't been revoked. Verify there are no typos or extra spaces. - Error:
message could not be sent because the originator is invalid
: EnsureMESSAGEBIRD_ORIGINATOR
in.env
matches a purchased number or a registered and approved Alphanumeric Sender ID in your MessageBird account. Check country restrictions for Alphanumeric Sender IDs. - Error:
Request parameter validation failed
(Parameter:recipients
): Ensure the recipient number is in a valid format. While MessageBird is somewhat flexible, E.164 format (+
followed by country code and number, e.g.,+14155552671
) is strongly recommended. The SDK expectsrecipients
as an array. - Error:
No (suitable) routes found
: You might have insufficient balance (for live keys) or the recipient country/network might not be supported by your MessageBird account setup or the originator type. Check your balance and MessageBird's coverage documentation. - SMS Not Received (Live Key):
- Verify recipient number is correct and has signal.
- Check MessageBird Dashboard Logs for detailed delivery status (e.g., ""delivered"", ""failed"", ""expired"").
- Ensure the number isn't on a Do-Not-Disturb list or blocked by the carrier.
- Check for country-specific regulations or carrier filtering.
- Callback vs. Promise: Remember the base MessageBird Node SDK uses callbacks. Our
Promise
wrapper is crucial for cleanasync/await
usage in NestJS. Errors in the promise handling logic can mask SDK errors. - Rate Limits: MessageBird imposes API rate limits. If sending high volumes, check their documentation and implement appropriate delays or throttling in your application. The
ThrottlerModule
helps protect your endpoint, but doesn't manage MessageBird's API limits directly. - SDK Version: Ensure you are using a reasonably recent version of the
messagebird
package, as APIs and methods can change.
10. Deployment and CI/CD
-
Build: Create a production build:
npm run build
This compiles TypeScript to JavaScript in the
dist
folder. -
Run Production: Start the application using Node.js directly on the compiled output:
node dist/main
-
Environment Variables: Ensure your production environment (e.g., Docker container, PaaS like Heroku/Vercel/AWS Elastic Beanstalk, VM) has the
MESSAGEBIRD_API_KEY
andMESSAGEBIRD_ORIGINATOR
environment variables set securely. Do not include the.env
file in your production artifact; use the platform's mechanism for environment variables. -
CI/CD Pipeline: Set up a pipeline (e.g., GitHub Actions, GitLab CI, Jenkins) to:
- Install dependencies (
npm ci
recommended for consistency). - Lint and format code.
- Run tests (
npm run test
). - Build the application (
npm run build
). - Deploy the
dist
folder,node_modules
,package.json
, and any other necessary files (like.env.production
if used, though platform env vars are better) to your hosting environment.
- Install dependencies (
-
Process Management: Use a process manager like
pm2
or rely on your platform's built-in service management (e.g., systemd, Docker orchestration) to keep the Node.js application running reliably, handle restarts, and manage logs.