Skip to content

Latest commit

Β 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“¨ SMS API Middleware

GitHub stars GitHub forks License: MIT

A secure, production-ready API middleware that acts as a gateway for SMS services with built-in encryption, rate limiting, and parameter-to-body conversion support for both GET and POST requests. Deployable on Node.js or Cloudflare Workers.

Node.js Cloudflare Workers Express.js TypeScript

πŸ“‹ Table of Contents

πŸš€ Features

Core Features

  • πŸ” AES-256 Encryption: Secure API key transmission with CBC mode encryption
  • ⏱️ Distributed Rate Limiting: Cloudflare Durable Objects for global rate limiting
  • πŸ”„ Smart Parameter Conversion: Automatically converts GET params or POST body to required format
  • πŸ“‘ Dual Method Support: Handles both GET and POST requests seamlessly
  • πŸ›‘οΈ Enterprise Security: Helmet.js, CORS, and input validation
  • πŸ“ Comprehensive Logging: Request/response logging for debugging
  • ⚑ Edge Computing: Deploy globally on Cloudflare's edge network
  • πŸ” Input Validation: Robust validation with meaningful error messages

Cloudflare-Specific Features

  • 🌍 Global Deployment: Deploy to Cloudflare's 300+ locations worldwide
  • 🎯 Edge Computing: Process requests closest to users
  • πŸ”„ Auto-scaling: Handles traffic spikes automatically
  • πŸ’° Cost-Effective: Pay-per-use pricing model
  • πŸš€ Zero Cold Starts: Always-on edge compute

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Client Request                          β”‚
β”‚                    (GET or POST with params)                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Cloudflare Workers Edge                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚   CORS &    β”‚   β”‚   API Key   β”‚   β”‚  Distributed Rate       β”‚β”‚
β”‚  β”‚   Preflight │──▢  Decryption │──▢  Limiting               β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                           β”‚                                     β”‚
β”‚                           β–Ό                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚            Parameter to Body Conversion                     β”‚β”‚
β”‚  β”‚       -> Extracts from query string or request body         β”‚β”‚
β”‚  β”‚       -> Case-insensitive parameter matching                β”‚β”‚
β”‚  β”‚       -> Validates required fields                          β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                           β”‚                                     β”‚
β”‚                           β–Ό                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚            Forward to SMS API                               β”‚β”‚
β”‚  β”‚       -> Adds decrypted API key to headers                  β”‚β”‚
β”‚  β”‚       -> Converts to POST request                           β”‚β”‚
β”‚  β”‚       -> Handles response and errors                        β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     SMS API Provider                            β”‚
β”‚                  (Twilio, Vonage, etc.)                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‹ Prerequisites

For Node.js Deployment

  • Node.js: v18.0.0 or higher
  • npm: v9.0.0 or higher
  • Environment: Linux, macOS, or Windows

For Cloudflare Workers Deployment

  • Cloudflare Account: Free or paid
  • Wrangler CLI: Latest version
  • Domain: (Optional) Custom domain for your worker

OpenSSL (Command Line)

  • Use OpenSSL to generate ENCRYPTION_KEY and ENCRYPTION_IV
  • Install OpenSSL in your local computer
  • Never use online generators for production keys

πŸ’» Installation

Generate encryption key

# Generate 32-character (256-bit) encryption key (ENCRYPTION_KEY)
openssl rand -hex 32 | cut -c1-32
# Example output: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

# Generate 16-character (128-bit) initialization vector (ENCRYPTION_IV)
openssl rand -hex 16 | cut -c1-16
# Example output: q8r9s0t1u2v3w4x5

Standard Node.js Installation

# Clone the repository
git clone https://github.com/ThiruXD/SMS-API-Middleware.git
cd SMS-API-Middleware

# Install dependencies
npm install

# Copy environment variables
cp .env.example .env

# Start development server
npm run dev

# Start production server
npm start

Cloudflare Workers Installation

# Clone the repository
git clone https://github.com/ThiruXD/SMS-API-Middleware.git
cd SMS-API-Middleware

# Install dependencies
npm install

# Login to Cloudflare
npx wrangler login

# Set up production secrets
npx wrangler secret put ENCRYPTION_KEY --env production
npx wrangler secret put ENCRYPTION_IV --env production
npx wrangler secret put SMS_API_KEY --env production
npx wrangler secret put SMS_API_URL --env production

# Optional: set staging secrets
npx wrangler secret put ENCRYPTION_KEY --env staging
npx wrangler secret put ENCRYPTION_IV --env staging
npx wrangler secret put SMS_API_KEY --env staging
npx wrangler secret put SMS_API_URL --env staging

# Deploy to Cloudflare Workers
npm run deploy:prod  # Deploy to production
npm run deploy:staging  # Deploy to staging

# For development with local testing
npm run dev

βš™οΈ Configuration

Environment Variables

Variable Description Default Required
ENCRYPTION_KEY AES-256 encryption key (32 chars) - βœ… Yes
ENCRYPTION_IV Initialization vector (16 chars) - βœ… Yes
SMS_API_URL Target SMS API URL - βœ… Yes
SMS_API_KEY Default API key - ❌ No
RATE_LIMIT_WINDOW_MS Rate limit window in milliseconds 900000 ❌ No
RATE_LIMIT_MAX_REQUESTS Maximum requests per window 100 ❌ No
PORT Server port (Node.js only) 5000 ❌ No
NODE_ENV Environment (development/production) development ❌ No

Cloudflare wrangler.toml Configuration

Important: vars are not inherited across Wrangler environments. If you deploy with --env production or --env staging, set required values in that specific environment section.

name = "sms-api-middleware"
main = "src/index.js"
compatibility_date = "2026-08-04"

# Global configurations (for local testing with 'wrangler dev')
[vars]
ENCRYPTION_KEY = "your-secret-encryption-key-32-chars-long"
ENCRYPTION_IV = "your-16-char-iv"
SMS_API_URL = "https://your-sms-api.com/api/v1/sms/send"
SMS_API_KEY = "your-default-api-key"
RATE_LIMIT_WINDOW_MS = "900000"
RATE_LIMIT_MAX_REQUESTS = "100"
NODE_ENV = "development"

[observability]
enabled = true
head_sampling_rate = 1.0

[[durable_objects.bindings]]
name = "RATE_LIMITER"
class_name = "RateLimiterDO"

[[migrations]]
tag = "v1"
new_sqlite_classes = ["RateLimiterDO"]


# ==========================================
# Environment-specific configurations
# ==========================================

[env.production]
[[env.production.durable_objects.bindings]]
name = "RATE_LIMITER"
class_name = "RateLimiterDO"

[env.production.vars]
NODE_ENV = "production"
SMS_API_URL = "https://your-sms-api.com"
ENCRYPTION_KEY = "your-secret-encryption-key-32-chars-long"
ENCRYPTION_IV = "your-16-char-iv"
SMS_API_KEY = "your-default-api-key"
RATE_LIMIT_WINDOW_MS = "900000"
RATE_LIMIT_MAX_REQUESTS = "100"

[env.staging]
[[env.staging.durable_objects.bindings]]
name = "RATE_LIMITER"
class_name = "RateLimiterDO"

[env.staging.vars]
NODE_ENV = "staging"
SMS_API_URL = "https://your-sms-api.com"
ENCRYPTION_KEY = "your-secret-encryption-key-32-chars-long"
ENCRYPTION_IV = "your-16-char-iv"
SMS_API_KEY = "your-default-api-key"
RATE_LIMIT_WINDOW_MS = "900000"
RATE_LIMIT_MAX_REQUESTS = "100"

☁️ Cloudflare Deployment

Step-by-Step Deployment Guide

1. Install and Configure Wrangler

# Login to Cloudflare
npx wrangler login

# Verify installation
npx wrangler --version

2. Set Up Secrets

# Set encryption key (32 characters)
npx wrangler secret put ENCRYPTION_KEY --env production

# Set encryption IV (16 characters)
npx wrangler secret put ENCRYPTION_IV --env production

# Set SMS API key
npx wrangler secret put SMS_API_KEY --env production

# Set SMS API URL
npx wrangler secret put SMS_API_URL --env production

3. Deploy to Cloudflare

# Development environment
npm run dev

# Production environment
npm run deploy:prod

# Staging environment
npm run deploy:staging

5. Configure Custom Domain (Optional)

  1. Go to Cloudflare Dashboard β†’ Workers & Pages
  2. Select your worker
  3. Go to Triggers β†’ Custom Domains
  4. Add your domain (e.g., api.yourdomain.com)

Monitoring & Management

# View logs
npm run logs

# Tail logs in real-time
npx wrangler tail --env production --format=pretty

# View worker metrics
# Go to Cloudflare Dashboard β†’ Analytics

πŸ“š API Documentation

Base URL

Local (Wrangler): http://localhost:8787
Cloudflare: https://your-worker.workers.dev

Endpoints

1. Send SMS

POST /api/send-sms
GET  /api/send-sms

# Route alias support
POST /send-sms
GET  /send-sms

Request Headers:

{
  "x-api-key": "encrypted_api_key",
  "Content-Type": "application/json"
}

x-api-key is required for /api/send-sms. Use /api/generate-key first, then pass the returned encryptedKey as the x-api-key header.

Request Parameters (for GET) or Body (for POST):

{
  "Sender_Name": "YourSenderName",
  "SMS_Message": "Your message content",
  "mobile_Number": "1234567890",
  "template_id": "your_template_id"
}

Note for GET requests: URL-encode + as %2B in mobile_Number.

Required Parameters:

Parameter Type Description Example Validation
Sender_Name string Sender name/ID "MyCompany" 3-50 chars
SMS_Message string SMS content "Hello World!" 1-1600 chars
mobile_Number string Recipient phone number "+1234567890" E.164 format
template_id string SMS template ID "tpl_12345" Alphanumeric

Success Response (200):

{
  "success": true,
  "data": {
    "messageId": "msg_123456789",
    "status": "sent",
    "recipient": "+1234567890"
  },
  "convertedFrom": "GET",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "responseStatus": 200
}

Error Responses:

Status Description Example
400 Bad Request Missing required parameters
401 Unauthorized Invalid API key
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Worker configuration issue
503 Service Unavailable SMS API timeout
504 Gateway Timeout SMS service took too long
530 Upstream DNS/Origin Error Cloudflare 1016 from upstream

2. Generate Encryption Key

POST /api/generate-key

# Route alias support
POST /generate-key

GET /generate-key is not supported. Use POST with a JSON body.

Request Body:

{
  "apiKey": "your_actual_api_key"
}

Response:

{
  "success": true,
  "encryptedKey": "U2FsdGVkX1/xxxxxxxxxxxxx",
  "originalKey": "your_actual_api_key"
}

3. Health Check

GET /api/health

# Route alias support
GET /health
GET /

Response:

{
  "status": "healthy",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "service": "SMS API Middleware",
  "environment": "production"
}

πŸ“ Usage Examples

cURL Examples

Node.js Deployment

# 1. Generate encrypted API key
curl -X POST "http://localhost:8787/api/generate-key" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "your-secret-api-key"}'

# 2. Send SMS via GET
curl -X GET "http://localhost:8787/api/send-sms?Sender_Name=Test&SMS_Message=Hello&mobile_Number=%2B1234567890&template_id=tpl_123" \
  -H "x-api-key: encrypted-key-here"

# 3. Send SMS via POST
curl -X POST "http://localhost:8787/api/send-sms" \
  -H "x-api-key: encrypted-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "Sender_Name": "Test",
    "SMS_Message": "Hello World",
    "mobile_Number": "+1234567890",
    "template_id": "tpl_123"
  }'

# 4. Health check
curl "http://localhost:8787/api/health"

Cloudflare Workers Deployment

# 1. Generate encrypted API key
curl -X POST "https://your-worker.workers.dev/api/generate-key" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "your-secret-api-key"}'

# 2. Send SMS via GET
curl -X GET "https://your-worker.workers.dev/api/send-sms?Sender_Name=Test&SMS_Message=Hello&mobile_Number=%2B1234567890&template_id=tpl_123" \
  -H "x-api-key: encrypted-key-here"

# 3. Send SMS via POST
curl -X POST "https://your-worker.workers.dev/api/send-sms" \
  -H "x-api-key: encrypted-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "Sender_Name": "Test",
    "SMS_Message": "Hello World",
    "mobile_Number": "+1234567890",
    "template_id": "tpl_123"
  }'

# 4. Root service status
curl "https://your-worker.workers.dev/"

JavaScript/TypeScript Examples

Using Fetch API

// POST request
async function sendSMS(data) {
  const response = await fetch('https://your-worker.workers.dev/api/send-sms', {
    method: 'POST',
    headers: {
      'x-api-key': 'encrypted-key-here',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  });
  
  return await response.json();
}

// Usage
const result = await sendSMS({
  Sender_Name: 'MyCompany',
  SMS_Message: 'Hello from JavaScript!',
  mobile_Number: '+1234567890',
  template_id: 'tpl_12345'
});

Using Axios

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://your-worker.workers.dev/api',
  headers: {
    'x-api-key': 'encrypted-key-here'
  }
});

// POST request
const response = await api.post('/send-sms', {
  Sender_Name: 'MyCompany',
  SMS_Message: 'Hello from Axios!',
  mobile_Number: '+1234567890',
  template_id: 'tpl_12345'
});

// GET request
const response = await api.get('/send-sms', {
  params: {
    Sender_Name: 'MyCompany',
    SMS_Message: 'Hello from Axios!',
    mobile_Number: '+1234567890',
    template_id: 'tpl_12345'
  }
});

Python Examples

import requests

# POST request
response = requests.post(
    'https://your-worker.workers.dev/api/send-sms',
    headers={
        'x-api-key': 'encrypted-key-here',
        'Content-Type': 'application/json'
    },
    json={
        'Sender_Name': 'MyCompany',
        'SMS_Message': 'Hello from Python!',
        'mobile_Number': '+1234567890',
        'template_id': 'tpl_12345'
    }
)

# GET request
response = requests.get(
    'https://your-worker.workers.dev/api/send-sms',
    headers={'x-api-key': 'encrypted-key-here'},
    params={
        'Sender_Name': 'MyCompany',
        'SMS_Message': 'Hello from Python!',
        'mobile_Number': '+1234567890',
        'template_id': 'tpl_12345'
    }
)

Mobile SDK Examples

// React Native
const sendSMS = async (params) => {
  try {
    const response = await fetch('https://your-worker.workers.dev/api/send-sms', {
      method: 'POST',
      headers: {
        'x-api-key': 'encrypted-key-here',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(params),
    });
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('SMS Error:', error);
  }
};

πŸ”’ Security

Encryption Implementation

The middleware uses AES-256-CBC encryption for API keys:

  • Algorithm: AES-256-CBC
  • Key Size: 32 bytes (256 bits)
  • IV Size: 16 bytes (128 bits)
  • Padding: PKCS7
  • Mode: CBC (Cipher Block Chaining)

Security Best Practices

  1. Key Management

    • Store encryption keys in environment variables or Cloudflare Secrets
    • Rotate keys regularly (every 90 days recommended)
    • Never commit keys to version control
    • Use different keys for different environments
  2. Rate Limiting

    • Configure appropriate limits based on your use case
    • Monitor rate limit violations
    • Adjust limits for different endpoints if needed
    • Use Cloudflare's built-in rate limiting for additional protection
  3. Input Validation

    • All parameters are validated before processing
    • Phone numbers are format-checked (E.164 format)
    • Required parameters are enforced
    • SQL injection and XSS protection
  4. HTTPS

    • Always use HTTPS in production
    • Configure SSL/TLS certificates
    • Enable HSTS headers
    • Use Cloudflare's SSL/TLS encryption
  5. Cloudflare-Specific Security

    • Enable WAF (Web Application Firewall)
    • Use Bot Management
    • Enable Rate Limiting at the edge
    • Use API Shield for API protection

Worker Response Headers

The worker always attaches CORS headers:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, x-api-key
Access-Control-Max-Age: 86400

Environment Variables

Always use environment variables or Cloudflare Secrets for sensitive data:

# Cloudflare Workers
npx wrangler secret put ENCRYPTION_KEY --env production
npx wrangler secret put ENCRYPTION_IV --env production
npx wrangler secret put SMS_API_KEY --env production
npx wrangler secret put SMS_API_URL --env production

# Node.js
ENCRYPTION_KEY=your-secret-key
ENCRYPTION_IV=your-iv
SMS_API_KEY=your-api-key

🚨 Error Handling

Error Codes

Status Code Description Retry?
200 Success N/A
400 Bad Request - Invalid parameters ❌ No
401 Unauthorized - Invalid API key ❌ No
429 Too Many Requests - Rate limit exceeded βœ… Yes (after cooldown)
500 Internal Server Error βœ… Yes
503 Service Unavailable - SMS API down βœ… Yes
504 Gateway Timeout βœ… Yes

Error Response Format

{
  "success": false,
  "error": "Descriptive error message",
  "requiredParams": ["param1", "param2"], // For validation errors
  "originalStatus": 500, // For upstream errors
  "details": "Additional error details", // For debugging
  "retryAfter": 60 // Seconds to wait before retry (rate limiting)
}

Implementing Retry Logic

async function sendSMSWithRetry(data, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await sendSMS(data);
      if (response.success) return response;
      
      // Handle rate limiting
      if (response.retryAfter) {
        await new Promise(resolve => setTimeout(resolve, response.retryAfter * 1000));
        continue;
      }
      
      // Handle server errors
      if (response.status >= 500) {
        await new Promise(resolve => setTimeout(resolve, attempt * 1000));
        continue;
      }
      
      // Client errors should not be retried
      return response;
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await new Promise(resolve => setTimeout(resolve, attempt * 1000));
    }
  }
}

πŸ“Š Monitoring & Logging

Request Logging

// Format for logging
{
  "timestamp": "2024-01-01T12:00:00.000Z",
  "method": "GET",
  "url": "/api/send-sms",
  "status": 200,
  "responseTime": "45ms",
  "apiKey": "Present",
  "convertedFrom": "QUERY_PARAMS"
}

Cloudflare Analytics

  1. Dashboard Analytics

    • Requests count
    • Status codes distribution
    • Response time
    • Traffic spikes
    • Error rates
  2. Custom Analytics

    // Add custom analytics
    ctx.waitUntil(
      analytics.writeDataPoint({
        blobs: ["sms_request", request.headers.get('cf-connecting-ip')],
        doubles: [1],
        indexes: ["sms_sent"]
      })
    );
  3. Third-Party Monitoring

    • Datadog: Use Cloudflare integration
    • New Relic: Use Cloudflare logs
    • Sentry: For error tracking
    • Prometheus: Export metrics

Health Check Endpoint

# Check service health
curl https://your-worker.workers.dev/api/health

# Response
{
  "status": "healthy",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "service": "SMS API Middleware",
  "environment": "production"
}

πŸ§ͺ Testing

Running Tests

# Run all tests
npm test

# Run tests with coverage
npm test -- --coverage

# Run specific test file
npm test -- src/middleware/encryption.test.js

# Watch mode (development)
npm run test:watch

Test Examples

import { describe, it, expect } from 'vitest';
import encryptionMiddleware from '../src/middleware/encryption.js';

describe('Encryption Middleware', () => {
  it('should encrypt and decrypt API key correctly', () => {
    const originalKey = 'test-api-key-123';
    const encrypted = encryptionMiddleware.encryptApiKey(originalKey);
    const decrypted = encryptionMiddleware.decryptApiKey(encrypted);
    expect(decrypted).toBe(originalKey);
  });
  
  it('should handle invalid encryption key gracefully', () => {
    expect(() => {
      encryptionMiddleware.decryptApiKey('invalid-key');
    }).toThrow();
  });
});

Load Testing

// Using k6 for load testing
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 }, // Ramp up
    { duration: '1m', target: 20 },  // Stay at 20 users
    { duration: '30s', target: 0 },  // Ramp down
  ],
};

export default function () {
  const response = http.get('https://your-worker.workers.dev/api/health');
  check(response, {
    'status is 200': (r) => r.status === 200,
  });
  sleep(1);
}

πŸ”§ Troubleshooting

Common Issues

1. Rate Limit Errors (429)

Symptoms: Requests fail with 429 status code

Solutions:

  • Reduce request frequency
  • Implement exponential backoff
  • Contact support to increase limits
  • Check if limit is per-IP or per-API-key

2. API Key Errors (401)

Symptoms: Requests fail with 401 status

Solutions:

  • Verify encryption key is valid
  • Check if API key is expired
  • Ensure proper encryption/decryption
  • Regenerate API key

3. Timeout Errors (504)

Symptoms: Requests timeout frequently

Solutions:

  • Increase timeout values
  • Check SMS API health
  • Implement circuit breaker
  • Use Cloudflare Workers' WaitUntil

4. Parameter Validation Errors (400)

Symptoms: Requests fail with 400 status

Solutions:

  • Check required parameters
  • Validate phone number format
  • Ensure proper encoding
  • Use correct parameter names

5. Worker timeout (Route handling timed out)

Symptoms: Response body includes:

{
  "success": false,
  "error": "Unhandled worker error",
  "details": "Route handling timed out"
}

Checklist:

  • Use method + path correctly (POST /api/generate-key, not GET /generate-key)
  • Ensure production secrets are configured with --env production
  • Tail the correct worker:
    • npx wrangler tail --env production --format=pretty
  • Verify upstream SMS_API_URL is reachable and not timing out

6. Upstream returns error code: 1016

Symptoms:

  • Tail logs include non-JSON upstream body such as error code: 1016
  • /api/send-sms returns an upstream error payload

Cause:

  • SMS_API_URL points to an unreachable origin, invalid hostname, or DNS record that Cloudflare cannot resolve.

Checklist:

  • Confirm SMS_API_URL is the real provider endpoint for production
  • Verify DNS/hostname of the upstream service resolves publicly
  • Test directly from local terminal:
    • curl -i "<SMS_API_URL>"
  • If the upstream is behind Cloudflare, verify its DNS/proxy/origin configuration

7. Wrangler warning: vars not inherited by environments

Symptoms:

  • Deploy/tail shows warning that top-level vars are not present in env.production.vars

Fix options:

  • Preferred: define runtime values in env.production.vars and env.staging.vars
  • Or duplicate required keys from top-level [vars] into each environment block
  • For sensitive values, use wrangler secret put ... --env <environment>

8. Upstream receives blank X-Api-Key

Symptoms:

  • Upstream request shows X-Api-Key: with no value
  • Middleware logs may show apiKey: 'Missing'

Cause:

  • Provided x-api-key header is missing, invalid ciphertext, or encrypted with a different ENCRYPTION_KEY/ENCRYPTION_IV than the worker uses.

Fix:

  • Always generate key from this worker: POST /api/generate-key
  • Use returned encryptedKey as x-api-key in /api/send-sms requests
  • Ensure same encryption secrets are configured for the target environment
  • If key is invalid, worker now returns 401 Invalid encrypted API key (instead of forwarding blank header)

🀝 Contributing

We welcome contributions! Please follow these guidelines:

Development Setup

  1. Fork the repository
  2. Clone your fork
  3. Install dependencies
  4. Create a feature branch
  5. Make your changes
  6. Run tests
  7. Submit a pull request

Commit Convention

We follow Conventional Commits:

# Examples
feat: add support for multiple SMS providers
fix: resolve rate limiting issue with Durable Objects
docs: update Cloudflare deployment documentation
style: format code with Prettier
refactor: optimize parameter extraction logic
test: add tests for rate limiter
chore: update dependencies

Code Style

# Check code style
npm run lint

# Fix code style issues
npm run lint:fix

# Format code
npm run format

Pull Request Process

  1. Update documentation if needed
  2. Add tests for new features
  3. Ensure all tests pass
  4. Request review from maintainers
  5. Keep pull requests focused and concise

Contributors:

  1. ThiruXD (Base)

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Support & Community

🌟 Show Your Support

If you found this project helpful, please give it a ⭐ on GitHub!

GitHub stars


Built with ❀️ for secure SMS API communication on Node.js and Cloudflare Workers

About

A secure, production-ready API middleware that acts as a gateway for SMS services with built-in encryption, rate limiting, and parameter-to-body conversion support for both GET and POST requests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages