Examples/Express.js

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