Express.js Integration
Build SMS applications with Express.js and sms-dev for local testing and development.
Quick Setup
Get started with Express.js and sms-dev in under 5 minutes.
1. Install Dependencies
# Install Express.js and dependencies npm init -y npm install express npm install -g @relay/sms-dev # Start sms-dev in the background sms-dev
2. Basic Express Server
Create a simple Express server with SMS capabilities
// server.js
const express = require('express');
const app = express();
const port = 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// SMS API client pointing to sms-dev
const SMS_API_URL = 'http://localhost:4001';
// Helper function to send SMS
async function sendSMS(to, from, body) {
try {
const response = await fetch(`${SMS_API_URL}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ to, from, body })
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to send SMS:', error);
throw error;
}
}
// Routes
app.get('/', (req, res) => {
res.json({
message: 'SMS Express Server',
status: 'running',
smsDevUrl: 'http://localhost:4000'
});
});
// Send SMS endpoint
app.post('/send-sms', async (req, res) => {
try {
const { to, from, body } = req.body;
if (!to || !from || !body) {
return res.status(400).json({
error: 'Missing required fields: to, from, body'
});
}
const result = await sendSMS(to, from, body);
res.json({
success: true,
messageId: result.id,
message: 'SMS sent successfully'
});
} catch (error) {
res.status(500).json({
error: 'Failed to send SMS',
details: error.message
});
}
});
// Webhook endpoint for receiving SMS replies
app.post('/webhook', (req, res) => {
const { from, to, body, messageId } = req.body;
console.log(`📱 Received SMS from ${from} to ${to}: ${body}`);
// Process the incoming message
handleIncomingSMS(from, to, body, messageId);
// Respond with 200 OK
res.status(200).send('OK');
});
// Handle incoming SMS logic
async function handleIncomingSMS(from, to, body, messageId) {
const message = body.toLowerCase().trim();
try {
if (message === 'help') {
await sendSMS(to, from,
'Available commands:\n' +
'• HELP - Show this menu\n' +
'• TIME - Get current time\n' +
'• JOKE - Get a random joke\n' +
'• STOP - Unsubscribe'
);
} else if (message === 'time') {
const now = new Date().toLocaleString();
await sendSMS(to, from, `Current time: ${now}`);
} else if (message === 'joke') {
const jokes = [
'Why do programmers prefer dark mode? Because light attracts bugs! 🐛',
'How many programmers does it take to change a light bulb? None, that\'s a hardware problem! 💡',
'Why do Java developers wear glasses? Because they can\'t C# 👓'
];
const randomJoke = jokes[Math.floor(Math.random() * jokes.length)];
await sendSMS(to, from, randomJoke);
} else if (message === 'stop') {
await sendSMS(to, from, 'You have been unsubscribed. Reply START to resubscribe.');
} else {
await sendSMS(to, from,
`Thanks for your message: "${body}"\n\n` +
'Reply HELP for available commands.'
);
}
} catch (error) {
console.error('Error handling incoming SMS:', error);
}
}
app.listen(port, () => {
console.log(`🚀 Server running at http://localhost:${port}`);
console.log(`📱 Virtual Phone: http://localhost:4000`);
console.log(`🔗 Webhook URL: http://localhost:${port}/webhook`);
});Advanced Features
Rate Limiting & Validation
Add rate limiting and input validation to your SMS endpoints
const rateLimit = require('express-rate-limit');
const { body, validationResult } = require('express-validator');
// Rate limiting middleware
const smsRateLimit = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // limit each IP to 10 SMS per windowMs
message: {
error: 'Too many SMS requests, please try again later.'
}
});
// Validation middleware
const validateSMS = [
body('to')
.isMobilePhone()
.withMessage('Invalid phone number format'),
body('from')
.isMobilePhone()
.withMessage('Invalid sender phone number format'),
body('body')
.isLength({ min: 1, max: 160 })
.withMessage('Message body must be between 1 and 160 characters'),
];
// Apply middleware to SMS endpoint
app.post('/send-sms',
smsRateLimit,
validateSMS,
async (req, res) => {
// Check validation results
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
error: 'Validation failed',
details: errors.array()
});
}
// Send SMS logic here...
}
);Database Integration
Store and retrieve SMS messages with MongoDB
const mongoose = require('mongoose');
// SMS Message Schema
const messageSchema = new mongoose.Schema({
messageId: { type: String, required: true, unique: true },
to: { type: String, required: true },
from: { type: String, required: true },
body: { type: String, required: true },
direction: { type: String, enum: ['inbound', 'outbound'], required: true },
status: { type: String, enum: ['sent', 'delivered', 'failed'], default: 'sent' },
timestamp: { type: Date, default: Date.now }
});
const Message = mongoose.model('Message', messageSchema);
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/sms_app');
// Enhanced send SMS with database logging
async function sendSMSWithLogging(to, from, body) {
try {
const result = await sendSMS(to, from, body);
// Save to database
const message = new Message({
messageId: result.id,
to,
from,
body,
direction: 'outbound',
status: 'sent'
});
await message.save();
console.log(`📝 Logged outbound message: ${result.id}`);
return result;
} catch (error) {
console.error('Failed to send and log SMS:', error);
throw error;
}
}
// Enhanced webhook with database logging
app.post('/webhook', async (req, res) => {
try {
const { from, to, body, messageId } = req.body;
// Save incoming message to database
const message = new Message({
messageId: messageId || `inbound_${Date.now()}`,
to,
from,
body,
direction: 'inbound'
});
await message.save();
console.log(`📝 Logged inbound message from ${from}`);
// Process the message
await handleIncomingSMS(from, to, body, messageId);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Internal Server Error');
}
});
// Get message history
app.get('/messages', async (req, res) => {
try {
const { phone, limit = 50 } = req.query;
let query = {};
if (phone) {
query = {
$or: [
{ to: phone },
{ from: phone }
]
};
}
const messages = await Message.find(query)
.sort({ timestamp: -1 })
.limit(parseInt(limit));
res.json({
messages,
total: messages.length
});
} catch (error) {
res.status(500).json({
error: 'Failed to retrieve messages',
details: error.message
});
}
});Running the Example
Step-by-Step Instructions
1
Start sms-dev
Run sms-dev --webhook-url http://localhost:3000/webhook
2
Start your Express server
Run node server.js
3
Send a test SMS
POST to http://localhost:3000/send-sms
4
Test replies
Use the Virtual Phone at http://localhost:4000
Test Request Example
Use curl or Postman to test your SMS endpoint
# Send an SMS message
curl -X POST http://localhost:3000/send-sms \
-H "Content-Type: application/json" \
-d '{
"to": "+1234567890",
"from": "+1987654321",
"body": "Hello from Express! Reply HELP for commands."
}'
# Response:
{
"success": true,
"messageId": "msg_abc123",
"message": "SMS sent successfully"
}