code examples
code examples
How to Send SMS with Node.js and Express: MessageBird API Tutorial (2025)
Learn how to send SMS messages using Node.js, Express.js, and MessageBird API. Step-by-step tutorial with code examples, E.164 phone validation, error handling, and production deployment tips. Updated for 2025.
MessageBird SMS with Express.js: Complete Integration Guide
⚠️ Important Notes (Updated January 2025):
SDK Maintenance Status: The
messagebirdnpm package (v4.0.1) has not been updated since 2022 and is no longer actively maintained. Consider this when choosing an SMS provider for new projects.Bird Rebranding: MessageBird rebranded as "Bird" in February 2024. While the legacy API and SDK remain functional, the company now operates as Bird (bird.com) with updated APIs and services. Check Bird's API documentation for current offerings.
Content Note: This guide covers MessageBird integration with Express.js. Despite the filename reference to Next.js and NextAuth, this article focuses on Express.js implementation.
Express.js Version: This guide is compatible with Express.js 5.x (requires Node.js ≥18).
This comprehensive tutorial shows you how to send SMS messages programmatically using Node.js, Express.js, and the MessageBird SMS API. Whether you're building two-factor authentication, notification systems, or marketing campaigns, you'll learn everything from initial setup to production deployment.
By the end of this guide, you'll have a working Express API endpoint that sends SMS messages to any phone number worldwide using MessageBird's reliable messaging infrastructure.
Project Goals:
- Create a Node.js Express server with SMS capabilities
- Integrate the MessageBird Node.js SDK for SMS delivery
- Securely manage API credentials using environment variables
- Build a REST API endpoint to trigger SMS sending
- Implement phone number validation and error handling
- Provide guidance on testing and production deployment
Technology Stack:
- Node.js: JavaScript runtime environment for server-side development
- Express.js: Minimal and flexible Node.js web application framework for building REST APIs
- MessageBird SMS API: Cloud-based SMS gateway providing reliable message delivery worldwide via the official Node.js SDK
- dotenv: Secure environment variable management for API keys and credentials
System Architecture:
The system consists of a User/Client (e.g., using cURL or a frontend application) making an HTTP request to the Node.js/Express App's API endpoint (e.g., /send-sms). The Express application receives the request, validates the input (recipient number, message body), and uses the MessageBird Node.js SDK. The SDK, configured with the API key securely loaded from an .env file, handles authentication and communicates with the MessageBird API. The MessageBird API processes the request and sends the SMS message to the recipient's mobile phone.
Prerequisites:
- Node.js and npm: Install Node.js 18 or higher (Express.js 5.x requires Node.js ≥18). Download Node.js
- MessageBird Account: Sign up for a free account at MessageBird.com or bird.com
- MessageBird API Key: Obtain your API key from the MessageBird Dashboard for authentication
- Note: MessageBird provides both live and test API keys. Test keys validate your API integration (checking authentication and request structure) without sending actual SMS messages or incurring costs, but they won't result in a message arriving on a phone. This guide uses a live key to demonstrate the full sending process.
- MessageBird Virtual Number: Purchase or use a trial virtual mobile number (VMN) capable of sending SMS from the MessageBird Dashboard – this will be your
originatornumber - Basic Terminal/Command Line Knowledge: Familiarity with navigating directories and running commands
- Code Editor: Such as VS Code, Sublime Text, or Atom
1. Setting Up Your Node.js SMS Project
Initialize your Node.js project and install the necessary dependencies for sending SMS messages.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir node-messagebird-sms cd node-messagebird-sms -
Initialize Node.js Project: Create a
package.jsonfile to track your project's metadata and dependencies.bashnpm init -y(The
-yflag accepts the default settings) -
Install Dependencies: Install Express for the web server, the MessageBird SDK, and
dotenvfor managing environment variables.bashnpm install express messagebird dotenv -
Create Project Files: Create the main application file and files for environment variables and Git ignore rules.
bashtouch index.js .env .gitignoreindex.js: Main application code.env: Stores sensitive information like API keys (configure later).gitignore: Specifies files Git should ignore (like.envandnode_modules)
-
Configure
.gitignore: Open.gitignoreand add these lines to prevent committing sensitive data and local dependencies:text# Dependencies node_modules/ # Environment variables .env # Logs npm-debug.log* yarn-debug.log* yarn-error.log* *.log # Optional Editor directories .vscode .idea -
Set Up Basic Express Server (
index.js): Openindex.jsand add the basic structure for an Express application.javascript// index.js require('dotenv').config(); // Load environment variables from .env file FIRST const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; // Use port from env var or default to 3000 // Middleware to parse JSON request bodies app.use(express.json()); // Simple root route app.get('/', (req, res) => { res.send('MessageBird SMS Sender API is running!'); }); // Start the server app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });- Why
dotenvfirst? Callrequire('dotenv').config()at the very top to ensure environment variables are loaded before any other code tries to access them. - Why
express.json()? This middleware is crucial for parsing incoming requests with JSON payloads (which you'll use for your API endpoint).
- Why
-
Run the Basic Server: Test if the basic setup works.
bashnode index.jsYou should see
Server listening on port 3000in your console. Openhttp://localhost:3000in your browser to see the welcome message. PressCtrl+Cto stop the server.
2. Integrating the MessageBird SMS API
Configure the MessageBird SDK and obtain the necessary credentials for sending SMS.
-
Obtain MessageBird API Key:
- Log in to your MessageBird Dashboard
- Navigate to the
Developerssection in the left sidebar - Click on the
API access (REST)tab - If you don't have a key, click
Add access key. Create or use a Live API Key if you intend to send real messages (refer to the Prerequisites note about test vs. live keys) - Copy the generated Live API Key – treat this key like a password and keep it secret
-
Obtain MessageBird Virtual Number (Originator):
- In the MessageBird Dashboard, navigate to
Numbersin the sidebar - If you don't have a number, click
Buy a number - Select your country and ensure the
SMScapability is enabled - Choose a number and complete the purchase (or use a trial number if available)
- Copy the full number in E.164 format (e.g.,
+12025550149,+442071838750) – this will be your sender ID (originator)
- In the MessageBird Dashboard, navigate to
-
Configure Environment Variables (
.env): Open the.envfile and add your API key and originator number. Replace the placeholder values with your actual credentials.bash# .env # Your LIVE MessageBird API Key MESSAGEBIRD_API_KEY=YOUR_LIVE_API_KEY_HERE # Your purchased MessageBird Virtual Number (in E.164 format) MESSAGEBIRD_ORIGINATOR_NUMBER=+12345678900MESSAGEBIRD_API_KEY: The live access key copied from the MessageBird dashboard – authenticates your requestsMESSAGEBIRD_ORIGINATOR_NUMBER: The virtual number you purchased or obtained – the 'sender' number that recipients will see (subject to country regulations). Note: In some countries, using an alphanumeric sender ID (likeMyCompany) is possible, but it often requires pre-registration and isn't universally supported. Using a virtual number is generally more reliable for programmatic sending.
-
Initialize MessageBird Client (
index.js): Modifyindex.jsto initialize the MessageBird client using the API key from environment variables.javascript// index.js require('dotenv').config(); const express = require('express'); // --- Add MessageBird initialization --- const messagebirdApiKey = process.env.MESSAGEBIRD_API_KEY; if (!messagebirdApiKey) { console.error('FATAL ERROR: MESSAGEBIRD_API_KEY environment variable not set.'); process.exit(1); // Exit if the key is missing } const messagebird = require('messagebird')(messagebirdApiKey); // --- End initialization --- const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); // --- Add Originator check --- if (!process.env.MESSAGEBIRD_ORIGINATOR_NUMBER) { console.warn('WARNING: MESSAGEBIRD_ORIGINATOR_NUMBER environment variable not set. Sending might fail or use default "MessageBird".'); // You might want to exit here too depending on requirements: // process.exit(1); } // --- End check --- app.get('/', (req, res) => { res.send('MessageBird SMS Sender API is running!'); }); // Placeholder for the SMS sending route (coming next) app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });- Why check environment variables? This simple check prevents the application from starting without essential configuration, making debugging easier.
3. Building the SMS Sending API Endpoint
Create the API endpoint that handles requests to send SMS messages programmatically.
-
Create the API Endpoint (
/send-sms): Add a newPOSTroute toindex.jsto handle SMS sending requests.javascript// index.js // ... (previous code: require statements, app init, middleware, checks) ... app.get('/', (req, res) => { res.send('MessageBird SMS Sender API is running!'); }); // --- Add SMS Sending Route --- app.post('/send-sms', (req, res) => { const { recipient, message } = req.body; // Extract recipient and message from request body // 1. Input Validation if (!recipient || !message) { console.warn('Send SMS failed: Missing recipient or message', { body: req.body }); return res.status(400).json({ error: 'Missing required fields: recipient and message' }); } // E.164 validation: Must start with '+' followed by country code (1-9) and 3-14 additional digits // Total length: 4-15 digits (including country code) // This matches MessageBird's required format if (!/^\+[1-9]{1}[0-9]{3,14}$/.test(recipient)) { console.warn('Send SMS failed: Invalid recipient format', { recipient }); return res.status(400).json({ error: 'Invalid recipient format. Use E.164 format (e.g., +12025551234).' }); } const originator = process.env.MESSAGEBIRD_ORIGINATOR_NUMBER || 'MessageBird'; // Use configured number or default // 2. Prepare Message Parameters const params = { originator: originator, recipients: [recipient], // Must be an array body: message, }; // 3. Send Message via MessageBird SDK console.log(`Attempting to send SMS to ${recipient} from ${originator}`); messagebird.messages.create(params, (err, response) => { if (err) { // 4. Handle Errors console.error('MessageBird API Error:', err); let statusCode = 500; let errorMessage = 'Failed to send SMS due to an internal server error.'; let errorCode = null; if (err.errors && err.errors.length > 0) { // Try to get specific error details from MessageBird response const firstError = err.errors[0]; errorCode = firstError.code; errorMessage = `MessageBird Error ${errorCode}: ${firstError.description}`; // Map common MessageBird error codes to HTTP status codes (selective mapping) if ([2, 9, 21, 22].includes(errorCode)) statusCode = 400; // Bad request if (errorCode === 7) statusCode = 402; // Payment Required if (errorCode === 10) statusCode = 401; // Unauthorized } else if (err.statusCode) { statusCode = err.statusCode; errorMessage = `Network or Client Error: ${err.message || 'Failed to communicate with MessageBird API'}`; } return res.status(statusCode).json({ error: errorMessage, errorCode: errorCode, details: err.errors || err.message }); } // 5. Handle Success console.log('MessageBird API Success:', response); const recipientInfo = response.recipients.items[0] || {}; res.status(200).json({ message: 'SMS submitted successfully!', details: { id: response.id, status: recipientInfo.status, // e.g., 'sent', 'scheduled' statusDatetime: recipientInfo.statusDatetime, recipient: recipientInfo.recipient, } }); }); }); // --- End SMS Sending Route --- app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });- Request Body: Expects a JSON body with
recipient(E.164 format phone number) andmessage(the text content) - Input Validation: Basic checks ensure required fields are present and the recipient number has a valid format – crucial for security and preventing errors
- Parameters: The
paramsobject maps directly to the requirements of the MessageBirdmessages.createfunction.recipientsmust be an array, even for a single number messagebird.messages.create: The core SDK function – takes the parameters and a callback function- Callback Function: The callback receives
err(if an error occurred during the API call) orresponse(on success) - Error Handling: If
errexists, log it and return a relevant HTTP status code (e.g., 400 for bad input, 402 for balance issues, 500 for server/API errors) and error message. Attempt to parse specific errors from the MessageBird response - Success Handling: If successful, log the response and return a 200 status with details like the MessageBird message ID and initial status
- Request Body: Expects a JSON body with
4. Testing Your SMS API with cURL and Postman
Test the endpoint using a tool like curl or Postman to verify SMS sending functionality.
-
Start the Server: Ensure your
.envfile is correctly configured with your live API key and originator number.bashnode index.js -
Send a Test Request (using
curl): Open a new terminal window (leave the server running in the first one). Replace+1YOUR_PHONE_NUMBERwith your actual mobile number in E.164 format.bashcurl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "recipient": "+1YOUR_PHONE_NUMBER", "message": "Hello from Node.js and MessageBird!" }' -
Check the Response:
- Success: You should receive a JSON response similar to this in your
curlterminal, and the SMS should arrive on your phone shortly (standard SMS charges may apply).Your server console (wherejson{ "message": "SMS submitted successfully!", "details": { "id": "mb-message-id-string", "status": "sent", "statusDatetime": "2023-10-27T10:30:00Z", "recipient": "+1YOUR_PHONE_NUMBER" } }node index.jsis running) should also show the success logs. - Error: If something is wrong (e.g., invalid API key, incorrect recipient format, insufficient funds), you'll get an error response:
json
// Example: Invalid recipient format { "error": "Invalid recipient format. Use E.164 format (e.g., +12025551234)." }Check the server console logs for more detailed error information.json// Example: MessageBird API error (e.g., bad key or insufficient balance) { "error": "MessageBird Error 7: No balance", "errorCode": 7, "details": [ /* ... detailed error object from MessageBird ... */ ] }
- Success: You should receive a JSON response similar to this in your
-
Testing with Postman:
- Open Postman
- Create a new request
- Set the method to
POST - Enter the URL:
http://localhost:3000/send-sms - Go to the "Body" tab, select "raw", and choose "JSON" from the dropdown
- Enter the JSON payload:
json
{ "recipient": "+1YOUR_PHONE_NUMBER", "message": "Testing SMS via Postman!" } - Click "Send". Observe the response in Postman and the logs in your server console
5. Error Handling and Logging Best Practices
While basic error handling using console.log/console.error works for development, production systems need more robustness. This section introduces improvements incorporated into the final code (Section 13).
- Consistent Error Format: Ensure all error responses follow a consistent JSON structure (e.g.,
{ "error": "message", "errorCode": 123, "details": { ... } }) - Structured Logging: Switch from
console.logto a dedicated logging library likewinstonorpino. This provides significant advantages:- Different Log Levels: Distinguish between informational messages (
info), warnings (warn), and critical errors (error) - Structured Output: Logging in JSON format makes logs easier to parse, query, and analyze by log management systems (like Datadog, Splunk, ELK stack)
- Configurable Transports: Send logs to different destinations (console, files, external services) based on environment or severity
- Example
winstonsetup (as used in Section 13):bashnpm install winstonjavascript// Example setup with Winston (replaces console.log/error) const winston = require('winston'); const logger = winston.createLogger({ level: 'info', // Log only info, warn, error by default format: winston.format.json(), // Log as JSON defaultMeta: { service: 'sms-sender' }, // Add service context transports: [ // Write errors to error.log new winston.transports.File({ filename: 'error.log', level: 'error' }), // Write all logs (info and above) to combined.log new winston.transports.File({ filename: 'combined.log' }), ], }); // Also log to the console in non-production environments if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple(), // Use simpler format for console })); } // Usage (replaces console.log/error): // logger.error('MessageBird API Error:', { error: err }); // logger.info('SMS submitted successfully', { recipient: recipient, messageId: response.id });
- Different Log Levels: Distinguish between informational messages (
- Retry Mechanisms: For critical messages where transient network errors are unacceptable, implement a retry strategy (e.g., using libraries like
async-retry) with exponential backoff. This adds complexity but improves reliability
6. Database Schema and Data Layer
For this basic SMS sending API endpoint, a database is not strictly required – messages are sent based on direct API requests.
However, if you were building a more comprehensive application (e.g., tracking message history, handling replies, managing user preferences), you would need a database (like PostgreSQL, MongoDB, MySQL). This would involve:
- Defining Schemas/Models: Structure for storing message details (ID, recipient, sender, body, status, timestamps), user data, etc.
- Data Access Layer: Using an ORM/ODM (like Sequelize for SQL, Mongoose for MongoDB) or plain database drivers to interact with the database
- Storing Message Status: Updating message status based on delivery reports received via MessageBird webhooks (a more advanced topic)
7. Security Features
Security is critical when building APIs that handle potentially sensitive information.
- Environment Variables: Never commit your
.envfile or hardcode API keys/credentials in your source code. Usedotenvfor local development and your deployment platform's secure environment variable management in production. Ensure.envis listed in your.gitignore - Input Validation: Rigorously validate all input from external sources (the request body in this case)
- Recipient: Ensure it adheres to the E.164 format. The regex
/^\+[1-9]{1}[0-9]{3,14}$/validates the format properly – it requires a+sign, followed by a country code digit (1–9), and 3–14 additional digits, for a total length of 4–15 digits. For maximum accuracy, consider libraries likegoogle-libphonenumber(though potentially overkill for a basic service) - Message Body: Check for reasonable length. SMS messages have character limits (typically 160 GSM-7 characters, 70 Unicode characters per part). Very long messages are split and cost more. Reject empty or excessively long messages. Sanitize content if it could contain malicious input, especially if derived from user-generated content elsewhere in your system
- Recipient: Ensure it adheres to the E.164 format. The regex
- Rate Limiting: Protect your API endpoint (and your MessageBird budget) from abuse (accidental or malicious). Use middleware like
express-rate-limitto limit the number of requests from a single IP address within a time window. This is implemented in the final code (Section 13)bashnpm install express-rate-limitjavascript// Example implementation (see Section 13 for full context) const rateLimit = require('express-rate-limit'); const smsLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 10, // Limit each IP to 10 requests per window message: { error: 'Too many SMS requests created from this IP, please try again after 15 minutes' } }); app.post('/send-sms', smsLimiter, /* route handler */ ); - Authentication/Authorization (If Applicable): If this API is not intended for public use, secure it. Ensure only authenticated and authorized clients (users, other services) can call the
/send-smsendpoint. Methods include API keys specific to your service, JWT tokens, session authentication, OAuth, etc. - HTTPS: Always deploy your application behind a proxy that terminates SSL/TLS, ensuring traffic between clients and your server is encrypted (HTTPS). Most modern hosting platforms handle this automatically
8. SMS Delivery Best Practices and Special Cases
- Originator Restrictions: Alphanumeric sender IDs (e.g., "MyCompany") are not supported in all countries (like the US and Canada) and often require pre-registration. Using a purchased virtual number (in E.164 format) as the
originatoris generally the most reliable approach for global sending. Always check Bird's country restrictions documentation (formerly MessageBird) for current regulations - Character Limits & Encoding: Standard SMS (using GSM-7 encoding) allows 160 characters. Using characters outside this set (like many emojis or non-Latin scripts) forces Unicode (UCS-2) encoding, reducing the limit to 70 characters per SMS part. MessageBird automatically handles splitting longer messages into multiple parts (concatenated SMS), but you are billed for each part. Inform users or truncate messages appropriately if length/cost is a concern
- Invalid or Non-Mobile Numbers: The E.164 validation helps, but MessageBird's API will also perform checks. An invalid or non-existent number will result in an API error (often
code: 2– invalid parameter) or a failed delivery status later - Blocked Numbers/Opt-outs: Implement mechanisms to handle user opt-outs (e.g., responding to "STOP" messages, maintaining a suppression list). Sending to users who have opted out can violate regulations (like TCPA in the US). MessageBird offers features to help manage opt-outs
9. Performance Optimizations
For this specific function (a single API call per request), performance bottlenecks within the Node.js code itself are less likely than external factors.
- Asynchronous Nature: Node.js is non-blocking. The
messagebird.messages.createcall is asynchronous, meaning your server isn't stalled waiting for MessageBird's response and can handle other requests concurrently. This is inherently performant for I/O-bound tasks - External API Latency: The primary performance factor will be the network latency and processing time of the MessageBird API itself
- Load Testing: If high throughput is expected, use load testing tools (e.g.,
k6,artillery,autocannon) to simulate traffic. This helps identify potential limits (CPU, memory, network bandwidth) or issues with dependencies (like rate limits) - Caching: Not typically applicable for sending unique SMS messages. Caching might be relevant if you were frequently fetching message statuses via API calls, but using MessageBird's webhooks for status updates is generally more efficient
10. Production Monitoring and Observability
To ensure your service runs reliably in production:
- Health Checks: Implement a simple
/healthendpoint that returns a200 OKstatus. Monitoring systems (like uptime checkers, Kubernetes liveness probes) can ping this endpoint to verify the service is operational (added in Section 13)javascript// Example health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() }); }); - Application Performance Monitoring (APM): Use APM tools (e.g., Datadog, New Relic, Dynatrace, Sentry APM, OpenTelemetry) to monitor key metrics like request latency, throughput (requests per minute), error rates, CPU/memory usage. These tools help diagnose performance issues and bottlenecks
- Structured Logging: As discussed in Section 5, use structured logging (e.g., Winston sending JSON to a log aggregator) for effective analysis and troubleshooting
- Error Tracking: Integrate dedicated error tracking services (Sentry, Bugsnag, Rollbar) to capture, aggregate, alert on, and provide context for runtime exceptions in your Node.js application
- MessageBird Dashboard: Regularly utilize the MessageBird Dashboard ("Messaging → Logs"). It provides invaluable insights into the status of each message (e.g.,
sent,delivered,failed), delivery timestamps, error codes if failures occur, and associated costs. This is crucial for debugging specific SMS delivery problems
11. Troubleshooting and Caveats
Common issues and things to be aware of:
- Error:
Authentication failedorRequest not allowed (incorrect login details…)(Code 2, 10, 21):- Cause: Invalid
MESSAGEBIRD_API_KEYin your environment variables. It might be mistyped, have extra whitespace, be a test key when a live one is needed, or belong to a different account - Solution: Double-check the API key in your
.envfile (or production environment variables) against the key shown in the MessageBird Dashboard (Developers→API access). Ensure you are using the correct key type (live/test). Restart your application after making changes
- Cause: Invalid
- Error:
recipient is invalidor similar parameter errors (Code 2, 22):- Cause: The phone number provided in the
recipientfield is not in the valid E.164 format (+sign followed by country code and number, no spaces or dashes) - Solution: Verify the input number format. Ensure your validation logic (regex or library) correctly enforces E.164. Check the data being sent in the request body
- Cause: The phone number provided in the
- Error:
originator is invalid(Code 2, 21):- Cause: The
MESSAGEBIRD_ORIGINATOR_NUMBERin your environment variables is incorrect, not associated with your MessageBird account, formatted incorrectly, or disallowed as a sender ID in the destination country - Solution: Confirm the number matches a number listed under
Numbersin your MessageBird Dashboard and is in E.164 format. If using an alphanumeric ID, check country regulations and registration status
- Cause: The
- Error:
No balance(Code 7):- Cause: Your MessageBird account lacks sufficient credits to cover the cost of the SMS message
- Solution: Add funds to your account through the MessageBird Dashboard (
Billingsection)
- SMS Not Received by Recipient:
- Cause: Can be varied: transient carrier delays, recipient's phone is off/out of service, incorrect number provided, carrier spam filtering, number is blocked, MessageBird platform issues (rare)
- Solution:
- Verify the recipient number is absolutely correct
- Check the MessageBird Dashboard logs (
Messaging→Logs) for that specific message. Look at its status (delivered,failed,expired) and any error details provided - Wait a few minutes – delivery isn't always instantaneous
- Test sending to a different number or carrier if possible to isolate the issue
- If logs show
deliveredbut the user insists they didn't receive it, the issue likely lies with the recipient's device or carrier - If logs show
failedwith an error code, investigate that specific code - Contact MessageBird support if issues persist despite logs showing
sentor if you suspect a platform problem
- Caveat: Cost: Sending SMS messages incurs costs based on the destination country and message volume. Monitor your MessageBird balance and be aware of pricing. Test keys are free but don't send real messages
- Caveat: Delivery Status: The API response provides an initial status (like
sentorscheduled). The final delivery confirmation (deliveredorfailed) happens asynchronously. To get these final statuses reliably, you need to implement webhooks to receive Delivery Reports from MessageBird (see "Next Steps"). The code examples provided map common API call errors, but a production system might need more comprehensive mapping or handling logic based on MessageBird's documentation
12. Deployment and CI/CD
To deploy your Node.js Express application:
- Choose a Hosting Platform: Many options exist, including:
- PaaS (Platform-as-a-Service): Heroku, Render, Fly.io (easy deployment from Git)
- Serverless: AWS Lambda, Google Cloud Functions, Vercel (often require framework adaptations)
- Containers: AWS Fargate/ECS, Google Cloud Run, Azure Container Apps (using Docker)
- VPS/Servers: AWS EC2, Google Compute Engine, DigitalOcean Droplets (require more manual setup)
- Prepare
package.json: Ensure yourscriptsinclude astartcommandjson// package.json excerpt { "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" } } - Environment Variables: Configure
MESSAGEBIRD_API_KEYandMESSAGEBIRD_ORIGINATOR_NUMBERsecurely within your chosen platform's settings. Never commit the.envfile to Git. Also setNODE_ENV=productionfor performance and security benefits - CI/CD Pipeline (Optional but Recommended): Automate testing, building, and deployment:
- Use services like GitHub Actions, GitLab CI/CD, CircleCI, or Jenkins
- Define workflows to run tests (
npm test) and deploy on successful builds to your hosting platform - Example GitHub Actions workflow (
.github/workflows/deploy.yml):yamlname: Deploy to Production on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - run: npm ci - run: npm test # Add your deployment steps here (e.g., Heroku deploy, AWS deploy)
- Health Check Endpoint: Ensure your application has a
/healthendpoint (see Section 10) that monitoring systems can use to verify service availability
13. Complete Production-Ready Code
Here's the full index.js with all best practices integrated:
// index.js - Production-Ready Version
require('dotenv').config();
const express = require('express');
const rateLimit = require('express-rate-limit');
// Initialize MessageBird
const messagebirdApiKey = process.env.MESSAGEBIRD_API_KEY;
if (!messagebirdApiKey) {
console.error('FATAL ERROR: MESSAGEBIRD_API_KEY environment variable not set.');
process.exit(1);
}
const messagebird = require('messagebird')(messagebirdApiKey);
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
// Rate limiting for SMS endpoint
const smsLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // Limit each IP to 10 requests per window
message: { error: 'Too many SMS requests from this IP, please try again after 15 minutes' },
standardHeaders: true,
legacyHeaders: false,
});
// Validation checks
if (!process.env.MESSAGEBIRD_ORIGINATOR_NUMBER) {
console.warn('WARNING: MESSAGEBIRD_ORIGINATOR_NUMBER not set. SMS sending may fail.');
}
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'UP',
timestamp: new Date().toISOString(),
service: 'messagebird-sms-api'
});
});
// Root endpoint
app.get('/', (req, res) => {
res.json({
message: 'MessageBird SMS API',
version: '1.0.0',
endpoints: {
health: '/health',
sendSms: 'POST /send-sms'
}
});
});
// SMS sending endpoint with rate limiting
app.post('/send-sms', smsLimiter, (req, res) => {
const { recipient, message } = req.body;
// Input validation
if (!recipient || !message) {
console.warn('Send SMS failed: Missing fields', {
hasRecipient: !!recipient,
hasMessage: !!message
});
return res.status(400).json({
error: 'Missing required fields: recipient and message'
});
}
// E.164 format validation
if (!/^\+[1-9]{1}[0-9]{3,14}$/.test(recipient)) {
console.warn('Send SMS failed: Invalid recipient format', { recipient });
return res.status(400).json({
error: 'Invalid recipient format. Use E.164 format (e.g., +12025551234).'
});
}
// Message length check (warn if > 160 chars)
if (message.length > 160) {
console.warn('Long message detected', {
length: message.length,
segments: Math.ceil(message.length / 160)
});
}
const originator = process.env.MESSAGEBIRD_ORIGINATOR_NUMBER || 'MessageBird';
const params = {
originator: originator,
recipients: [recipient],
body: message,
};
console.log(`Sending SMS to ${recipient} from ${originator}`);
messagebird.messages.create(params, (err, response) => {
if (err) {
console.error('MessageBird API Error:', {
errors: err.errors,
statusCode: err.statusCode
});
let statusCode = 500;
let errorMessage = 'Failed to send SMS due to an internal server error.';
let errorCode = null;
if (err.errors && err.errors.length > 0) {
const firstError = err.errors[0];
errorCode = firstError.code;
errorMessage = `MessageBird Error ${errorCode}: ${firstError.description}`;
// Map error codes to HTTP status codes
if ([2, 9, 21, 22].includes(errorCode)) statusCode = 400;
if (errorCode === 7) statusCode = 402;
if (errorCode === 10) statusCode = 401;
} else if (err.statusCode) {
statusCode = err.statusCode;
errorMessage = `Network error: ${err.message || 'Failed to communicate with MessageBird API'}`;
}
return res.status(statusCode).json({
error: errorMessage,
errorCode: errorCode,
details: err.errors || err.message
});
}
console.log('SMS sent successfully:', {
messageId: response.id,
recipient: recipient,
status: response.recipients.items[0]?.status
});
const recipientInfo = response.recipients.items[0] || {};
res.status(200).json({
message: 'SMS submitted successfully!',
details: {
id: response.id,
status: recipientInfo.status,
statusDatetime: recipientInfo.statusDatetime,
recipient: recipientInfo.recipient,
}
});
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Endpoint not found' });
});
// Error handler
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({ error: 'Internal server error' });
});
// Start server
app.listen(PORT, () => {
console.log(`MessageBird SMS API listening on port ${PORT}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});14. Next Steps and Advanced Features
To extend your SMS application beyond basic sending:
- Delivery Reports (DLRs) via Webhooks: Implement webhook endpoints to receive real-time delivery status updates from MessageBird. This allows you to track when messages are delivered, failed, or expired
- Inbound SMS Handling: Set up webhooks to receive incoming SMS messages, enabling two-way communication (e.g., for customer support, surveys, or STOP/opt-out processing)
- Message Scheduling: Schedule SMS messages to be sent at specific times using MessageBird's scheduling parameters
- Bulk SMS Sending: Optimize for sending large volumes of messages by batching requests or using MessageBird's bulk sending features
- Database Integration: Store message history, track delivery statuses, and manage contact lists using a database (PostgreSQL, MongoDB, etc.)
- Template Management: Create reusable message templates with variable substitution for personalized bulk messaging
- Multi-Channel Messaging: Extend beyond SMS to include WhatsApp, Voice, or Email using MessageBird's (Bird's) unified API
- A/B Testing: Test different message variations to optimize engagement and conversion rates
- Compliance Management: Implement opt-in/opt-out workflows, maintain suppression lists, and ensure GDPR/TCPA compliance
Related Resources
- Node.js SMS Integration Best Practices
- E.164 Phone Number Format Guide
- SMS Authentication and 2FA Implementation
- Building Notification Systems with Node.js
- Express.js REST API Tutorial
Ready to send SMS with Node.js? Follow this tutorial to build your own production-ready SMS API using Express.js and MessageBird. Whether you're implementing authentication codes, notifications, or marketing campaigns, this comprehensive guide covers everything you need to know.
Frequently Asked Questions
How to send SMS with Node.js and Express
Set up an Express server, install the MessageBird SDK and dotenv, configure your .env file with API keys, and create a /send-sms endpoint to handle requests. This endpoint will use the MessageBird SDK to send messages based on the provided recipient and message body. The article provides a detailed walkthrough of this process.
What is MessageBird used for in Node.js
MessageBird is a messaging platform that provides APIs for various communication channels, including SMS. Its Node.js SDK simplifies the process of integrating SMS functionality into your Node.js and Express applications. You'll use their REST API via the official Node.js SDK.
Why does dotenv matter for MessageBird integration
Dotenv is essential for securely managing your MessageBird API keys. It loads environment variables from a .env file, preventing you from hardcoding sensitive credentials directly into your application code, thus enhancing security.
When should I validate recipient phone numbers
Always validate recipient phone numbers upon receiving them in your /send-sms endpoint. This prevents sending messages to invalid numbers, which can result in errors or unnecessary charges. Use a regex or a specialized library like google-libphonenumber for comprehensive validation.
Can I use an alphanumeric sender ID with MessageBird
Alphanumeric sender IDs are not universally supported and may be restricted or require pre-registration in certain countries. For reliable global SMS sending, use a purchased virtual number as the originator, which is supported across more regions.
How to set up a MessageBird SMS endpoint in Express
Create a POST route (/send-sms) in your Express app that extracts recipient and message data from the request body. Validate the inputs, prepare the parameters, and then use messagebird.messages.create() to send the SMS through the MessageBird API.
What is the role of express.json() middleware
The express.json() middleware is crucial for parsing incoming request bodies in JSON format, allowing your Express application to access the recipient and message data submitted to the /send-sms endpoint.
How to handle MessageBird API errors in Node.js
Implement robust error handling in your /send-sms route's callback function. Check for errors returned by the MessageBird API and respond with appropriate HTTP status codes and informative error messages. Logging errors with details is also crucial for debugging.
Why use environment variables for API keys
Storing API keys in environment variables, managed by dotenv locally and through your deployment platform in production, ensures that sensitive credentials are not exposed in your codebase or version control system.
How to test the MessageBird SMS integration locally
Use tools like curl or Postman to send POST requests to your /send-sms endpoint with test data. Check the server console for logs and the response for success or error messages. Verify that the SMS arrives on the recipient's phone (if using a live API key).
When to use a logging library like Winston
For production applications, switch from console.log to a structured logging library like Winston. This allows for different log levels, JSON-formatted output for easier analysis, and configurable transports to send logs to various destinations.
What are some common MessageBird API error codes
Common errors include code 2 for invalid parameters (like recipient format or originator), code 7 for insufficient balance, and code 10 for authentication failures (invalid API key). Refer to the MessageBird API documentation for a complete list of error codes.
How to implement rate limiting for SMS sending
Use middleware like express-rate-limit to restrict the number of requests from a single IP address within a specified time window, preventing abuse and protecting your MessageBird budget. Configure the middleware with appropriate limits and error messages.
What security measures to consider for SMS API
Essential security measures include using environment variables for API keys, validating all user inputs, implementing rate limiting, enforcing HTTPS, and adding authentication/authorization if the API is not public.
How to troubleshoot SMS not received by recipient
Check the MessageBird Dashboard logs for message status and any error codes. Verify the recipient number, consider carrier delays, and contact MessageBird support if necessary.