← Index Case study 03

Mailing Service

Test email gateway with multi-provider failover and OTP extraction

Stack
TypeScriptFastifyPostgreSQLRedisDocker
fig. — simulated system behaviour live sim
Problem

E2E test suites that verify email flows (signup, OTP, password reset) need disposable inboxes and reliable OTP extraction. Each email provider has different APIs, quotas, and failure modes. Tests shouldn't break when a provider goes down.

Solution

A REST API that abstracts four email providers behind one interface: allocate an inbox, wait for a matching email, extract the OTP. Built-in failover, circuit breakers, quota tracking, and provider health monitoring.

Architecture
architecture
Client → REST API (Fastify)
     ├── ProviderManager
     │   ├── Mailpit (local, Docker)
     │   ├── MailSlurp (cloud)
     │   ├── Mailtrap (cloud)
     │   └── Mailinator (cloud, disabled by default)
     ├── OTP Extractor (regex, VM-sandboxed)
     ├── PostgreSQL (inbox metadata, usage tracking)
     └── Redis (message cache, rate limiting)
Key Decisions

Fastify over Express

Schema validation, performance, TypeScript-first.

Provider abstraction

BaseProvider interface — new providers need only implement 5 methods.

Circuit breakers per provider

Automatic cooldown after failures prevents cascading outages.

Priority-based provider selection

Quota-aware routing picks the best available provider per request.

VM-sandboxed regex execution

Custom OTP patterns run in an isolated VM with a 50ms time limit.

Constant-time API key comparison

SHA-256 hashing with timing-attack resistant comparison.

API Surface
GET /get-a-mail Allocate inbox (provider, domain, TTL)
GET /get-otp Wait for email, extract OTP
POST /wait-for-email Wait for matching message
GET /inbox/{email} Inbox metadata
DEL /inbox/{email} Release inbox
GET /messages Message metadata
GET /providers Provider health and quota status
Request / Response
GET /get-otp
// Request
GET /get-otp?email=test-9f3a@mailpit.local&timeout=30000

// Response 200
{
  "success": true,
  "otp": "847291",
  "source": {
    "from": "noreply@example.com",
    "subject": "Your verification code",
    "provider": "mailpit",
    "received_at": "2025-11-14T09:32:15.442Z"
  },
  "extraction": {
    "pattern": "default_6digit",
    "confidence": 1.0,
    "duration_ms": 12
  }
}
Stack
Runtime

TypeScript, Node.js 20+, Fastify 5

Data

PostgreSQL (metadata), Redis (cache, rate limits)

Testing

Vitest (mocked providers, no network)

Infrastructure

Docker Compose (API + Postgres + Redis + Mailpit)

Security

SHA-256 API keys, log redaction, inbox isolation

Next case study

Terminal Trading Platform

→