npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@power-rent/phone-validation-adapter

v1.0.0

Published

REST API for phone number validation using Twilio Lookup API

Downloads

43

Readme

Phone Validation Adapter

REST API adapter for phone number validation using Twilio Lookup API. This project was created to solve a critical issue: legacy applications running on Node.js < 14 cannot directly install the Twilio SDK due to version incompatibility. This adapter provides a simple REST interface that allows legacy systems to validate phone numbers by calling this service, eliminating the need to upgrade their entire Node.js infrastructure.

Setup

Prerequisites

  • Node.js >= 18.0.0
  • Twilio Account with API credentials

Installation

npm install

Environment Variables

Create a .env file in the root directory:

TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
PORT=3000

Important: The TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN are required. Without them, the validation service will return HTTP 503.

API Key Configuration

All requests to the validation endpoint require an API key and project name for authentication. API keys are stored in src/validApiKeys.ts:

const validApiKeys: Record<string, string> = {
  RLC: process.env.API_KEY_RLC || '',
  TJS: process.env.API_KEY_TJS || ''
};

Project Names:

  • RLC - Power Rent Rental Company
  • TJS - TJ Services

To add a new project:

  1. Add environment variable to .env:
API_KEY_NEW_PROJECT="your-secret-api-key"
  1. Update src/validApiKeys.ts:
const validApiKeys: Record<string, string> = {
  RLC: process.env.API_KEY_RLC || '',
  TJS: process.env.API_KEY_TJS || '',
  NEW_PROJECT: process.env.API_KEY_NEW_PROJECT || ''
};
  1. Rebuild and restart:
npm run build
npm run start

Important Security Notes:

  • Never commit actual API keys to version control
  • Store keys in environment variables or a secrets manager
  • Each client application should have its own unique key
  • Rotate keys regularly (every 90 days recommended)

API Endpoints

POST /validate

Validates a phone number using Twilio Lookup API.

Authentication Required: x-api-key and x-project-name headers with valid credentials.

Request

curl -X POST http://localhost:3000/validate \
  -H "Content-Type: application/json" \
  -H "x-api-key: apple-blossom-elephant-rocket" \
  -H "x-project-name: RLC" \
  -d '{"phoneNumber": "+1234567890"}'

Request body:

{
  "phoneNumber": "+1234567890"
}

Request headers:

| Header | Value | Required | Description | |--------|-------|----------|-------------| | x-api-key | API Key | Yes | Project-specific API key for authentication | | x-project-name | Project Name | Yes | Project identifier (RLC or TJS) | | Content-Type | application/json | Yes | Request content type |

Response (Success - HTTP 200)

{
  "success": true,
  "isValid": true
}

Response (Invalid Number - HTTP 200)

{
  "success": true,
  "isValid": false
}

Response (Unauthorized - HTTP 401)

Returned when API key or project name is missing or invalid:

{
  "success": false,
  "error": "Invalid API key"
}

Or:

{
  "success": false,
  "error": "Invalid project name"
}

Response (Client Error - HTTP 400)

{
  "success": false,
  "error": "phoneNumber is required and must be a non-empty string"
}

Response (Service Unavailable - HTTP 503)

Returned when Twilio credentials are not configured:

{
  "success": false,
  "error": "Validation service is temporarily unavailable"
}

Response (Server Error - HTTP 500)

Returned when Twilio API fails or times out:

{
  "success": false,
  "error": "Phone validation failed. Please try again later."
}

Error Handling Flow

This diagram shows how errors are handled at different stages:

POST /validate
│
├─ API Key Validation
│  ├─ x-project-name header missing?
│  │  └─ HTTP 401 (Unauthorized)
│  │     └─ Client error: invalid project name
│  │
│  ├─ x-project-name exists in whitelist?
│  │  └─ Continue to x-api-key validation
│  │
│  ├─ x-api-key header missing?
│  │  └─ HTTP 401 (Unauthorized)
│  │     └─ Client error: invalid API key
│  │
│  └─ x-api-key matches project?
│     └─ Continue to Input validation
│
├─ Input Validation
│  ├─ Missing or empty phoneNumber?
│  │  └─ HTTP 400 (Bad Request)
│  │     └─ Client error: invalid input
│  │
│  └─ phoneNumber is valid string?
│     └─ Continue to Twilio validation
│
├─ Configuration Check (twilioLookupValidation.ts)
│  ├─ TWILIO_ACCOUNT_SID missing?
│  │  ├─ Throw ConfigurationError
│  │  ├─ Log to Sentry
│  │  └─ server.ts catches it
│  │     └─ HTTP 503 (Service Unavailable)
│  │        └─ User sees: "Service temporarily unavailable"
│  │
│  ├─ TWILIO_AUTH_TOKEN missing?
│  │  ├─ Throw ConfigurationError
│  │  ├─ Log to Sentry
│  │  └─ server.ts catches it
│  │     └─ HTTP 503 (Service Unavailable)
│  │
│  └─ Credentials OK?
│     └─ Call Twilio API with 5s timeout
│
├─ Twilio API Call
│  ├─ Request times out (> 5s)?
│  │  ├─ Throw ValidationError
│  │  ├─ Log to Sentry
│  │  └─ server.ts catches it
│  │     └─ HTTP 500 (Internal Server Error)
│  │        └─ User sees: "Validation failed, try again"
│  │
│  ├─ Twilio API returns error?
│  │  ├─ Throw ValidationError
│  │  ├─ Log to Sentry
│  │  └─ server.ts catches it
│  │     └─ HTTP 500 (Internal Server Error)
│  │
│  ├─ Phone is valid?
│  │  └─ Return true
│  │     └─ HTTP 200 + isValid: true
│  │        └─ User can submit form
│  │
│  └─ Phone is invalid?
│     └─ Return false
│        └─ HTTP 200 + isValid: false
│           └─ User sees validation error message
│
└─ Error Logging (All paths)
   └─ Sentry with error type tag
      ├─ ConfigurationError → errorType: 'configuration'
      └─ ValidationError → errorType: 'validation'

Key Design Decisions

1. Explicit Error Types

Two custom error classes distinguish between different failure modes:

  • ConfigurationError: Missing Twilio credentials (HTTP 503)
  • ValidationError: Twilio API failures or timeouts (HTTP 500)

This prevents silent failures where configuration problems would be masked as valid numbers.

2. Timeout Protection

Twilio API calls have a 5-second timeout. If exceeded:

  • ValidationError is thrown
  • HTTP 500 is returned
  • User is informed of the failure

3. No Silent Failures

Previous implementation returned true for missing credentials. This caused:

  • Users to believe their phone was validated
  • Form submissions with invalid phone numbers
  • Data quality issues

Current implementation:

  • Fails explicitly with HTTP 503
  • Informs users the service is unavailable
  • Prevents invalid data submission

Architecture & Security

This adapter is designed as a backend microservice that bridges the gap between legacy applications and Twilio. Understanding the architecture is critical for proper deployment and security.

Client-Server Architecture

┌─────────────────────────────────┐
│  Client Application             │
│  (Frontend/Backend/Legacy app)  │
└────────────┬────────────────────┘
             │ HTTP POST /validate
             │ with x-api-key header
             ↓
┌─────────────────────────────────────────────┐
│  Phone Validation Adapter                   │
│  (this service)                             │
│  ✓ Validates API key                        │
│  ✓ Validates phone number format            │
│  ✓ Calls Twilio API                         │
│  ✓ Returns validation result                │
└────────────┬────────────────────────────────┘
             │
             ↓
     ┌───────────────────┐
     │   Twilio API      │
     └───────────────────┘

Backend Responsibilities

The backend service (this adapter) is responsible for:

  1. Store API Keys Securely

    API keys should never be hardcoded in production. Instead, use one of these approaches:

    • Environment Variables (recommended for simple deployments):

      VALID_API_KEYS=key1,key2,key3
    • Secrets Manager (AWS Secrets Manager, HashiCorp Vault, etc.):

      // Pseudo-code
      const keys = await secretsManager.getSecret('phone-validation-keys');
    • Database (for dynamic key management):

      // Pseudo-code
      const keys = await database.query('SELECT api_key FROM valid_keys WHERE active = true');
  2. Issue Keys to Clients

    Provide a secure endpoint where authenticated clients can request API keys:

    // Pseudo-code example
    app.post('/api-keys/request', (req, res) => {
      // Verify client identity (JWT, OAuth, mTLS, etc.)
      // Generate or retrieve API key
      // Return key to client
      res.json({ apiKey: 'generated-key' });
    });

    Important: This endpoint should:

    • Require strong authentication (JWT, OAuth, mTLS)
    • Rate limit requests
    • Log all key requests
    • Expire keys automatically
    • Allow key rotation
  3. Validate Keys Before Processing

    Always verify API keys on the server before calling Twilio:

    // Current implementation does this via middleware
    const authenticateApiKey = (req, res, next) => {
      const apiKey = req.headers['x-api-key'];
      const projectName = req.headers['x-project-name'];
         
      if (!checkIfKeyExists(projectName)) {
        res.status(401).json({ error: 'Invalid project name' });
        return;
      }
         
      if (!isValidApiKey({ key: apiKey, projectName })) {
        res.status(401).json({ error: 'Invalid API key' });
        return;
      }
      next();
    };

    Benefits:

    • Keys never exposed to clients
    • Project-specific key validation
    • Prevents unauthorized Twilio API calls
    • Reduces costs (Twilio charges per request)
    • Enables per-project rate limiting
    • Audit trail for all validation attempts

Frontend/Client Responsibilities

Client applications should:

  1. Never hardcode API keys in frontend code
  2. Request keys from backend via secure endpoint
  3. Pass keys in request headers (x-api-key)
  4. Handle 401 responses gracefully
  5. Implement retry logic for transient failures

Example client usage:

// Get API key from backend (must be authenticated)
const apiKey = await fetch('/api/phone-validation-key', {
  headers: { Authorization: 'Bearer ' + userToken }
}).then(r => r.json()).then(r => r.apiKey);

// Get project name from backend config or user settings
const projectName = 'RLC'; // or 'TJS'

// Use key to validate phone
const result = await fetch('https://validator-service.example.com/validate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': apiKey,
    'x-project-name': projectName
  },
  body: JSON.stringify({ phoneNumber: '+1234567890' })
});

Security Best Practices

  1. Rotate API Keys Regularly - Change keys every 90 days
  2. Use HTTPS - All communication must be encrypted
  3. Rate Limit - Prevent abuse of the validation endpoint
  4. Monitor Usage - Track validation requests and costs
  5. Separate Credentials - Twilio keys ≠ Adapter keys
  6. Audit Logging - Log all API key usage
  7. Principle of Least Privilege - Give clients minimum permissions needed

Development

Start Development Server

npm run dev

Server runs on http://localhost:3000 by default.

Build

npm run build

Output goes to dist/ directory.

Testing

npm run lint
npm run format:check

License

MIT