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" }