Skip to content

Latest commit

 

History

History
298 lines (235 loc) · 7.33 KB

File metadata and controls

298 lines (235 loc) · 7.33 KB

Quick Reference

Testing the System

1. Start Services

docker-compose up --build

2. Register Account (get API key)

curl -X POST http://localhost:3000/accounts/register \
  -H "Content-Type: application/json" \
  -d '{
    "account_name": "Test Co",
    "monthly_limit": 10000,
    "webhook_url": "https://example.com/webhook"
  }'

Save the returned api_key value.

3. Send Email

API_KEY="sk_..."

curl -X POST http://localhost:3000/emails/send \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "test@example.com",
    "subject": "Test Email",
    "html": "<p>This is a test</p>",
    "idempotency_key": "test-1"
  }'

Save the returned email_id.

4. Check Email Status

EMAIL_ID="..."

curl http://localhost:3000/emails/$EMAIL_ID \
  -H "Authorization: Bearer $API_KEY"

5. Check Usage

curl http://localhost:3000/accounts/usage \
  -H "Authorization: Bearer $API_KEY"

6. Check Health

curl http://localhost:3000/health

Returns 200 if everything is ok.


Key Features

Email Reliability

  • Idempotency prevents duplicates (same request = same email)
  • 5 automatic retries on send failure
  • Append-only billing (charge only on success)

Webhooks

  • email.sent - Triggered when email delivered
  • email.failed - Triggered after all retries exhausted
  • 3 retries with exponential backoff

Rate Limiting

  • 100 requests/minute per account
  • X-RateLimit-* headers in responses

Security

  • Cryptographically secure API keys
  • Account-scoped access (can't see others' emails)
  • Input validation on all endpoints

Resilience

  • Automatic database reconnection
  • Service health checks
  • Graceful shutdown

File Structure

src/config/env.ts          ← Configuration validation
src/db/index.ts            ← Database connection & health
src/db/schema.sql          ← Database tables
src/middleware/apiKeyAuth.ts  ← Bearer token auth
src/middleware/rateLimit.ts   ← Request rate limiting
src/routes/accounts.ts     ← Account & API key endpoints
src/routes/emails.ts       ← Email endpoints
src/services/usageService.ts  ← Billing tracking
src/workers/emailWorker.ts ← Background email processor
src/app.ts                 ← Express setup
src/server.ts              ← Server startup/shutdown
tests/integration.test.ts  ← Integration tests

Common Commands

# Build TypeScript
npm run build

# Start production server
npm start

# Start development with auto-reload
npm run dev

# Run integration tests
npm test

# Docker commands
docker-compose up --build         # Start all services
docker-compose logs -f api        # Watch API logs
docker-compose logs -f postgres   # Watch database logs
docker-compose logs -f redis      # Watch cache logs
docker-compose down               # Stop all services

Error Codes

Code Meaning
200 Success
201 Created
202 Accepted (email queued)
400 Bad request (invalid input)
401 Unauthorized (missing/invalid API key)
404 Not found (email doesn't exist)
429 Too many requests (rate limited)
503 Service unavailable (database down)

Database Schema

-- Accounts (with webhook support)
accounts (id, account_name, webhook_url, monthly_limit, created_at)

-- API Keys
api_keys (id, account_id, key_hash, key_prefix, created_at)

-- Emails
emails (id, account_id, to, subject, html, status, attempts, 
        error_details, provider_message_id, created_at, sent_at)

-- Usage tracking (append-only)
email_usage (id, account_id, month, email_count, created_at)

Environment Variables

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@postgres:5432/mail
REDIS_URL=redis://redis:6379
RESEND_API_KEY=re_xxxxxxxxxxxx

Architecture Overview

┌─────────────┐
│  Client     │
└──────┬──────┘
       │ HTTP
       ▼
┌──────────────────────────────┐
│   Express API Server         │
│  ├─ Request ID Tracing       │
│  ├─ API Key Auth             │
│  ├─ Rate Limiting            │
│  └─ Error Handling           │
└──────┬──────────────────┬────┘
       │                  │
       ▼ SQL              ▼ Commands
  ┌─────────────┐    ┌──────────────┐
  │ PostgreSQL  │    │ Redis Queue  │
  │  Database   │    │ (BullMQ)     │
  └─────────────┘    └──────┬───────┘
                            │ Process Jobs
                            ▼
                    ┌──────────────────┐
                    │ Email Worker     │
                    │ ├─ Send (5x)     │
                    │ ├─ Webhook (3x)  │
                    │ └─ Record Usage  │
                    └──────┬───────────┘
                           │ HTTP POST
                           ▼
                    ┌──────────────────┐
                    │ Resend Provider  │
                    │ + Customer       │
                    │   Webhooks       │
                    └──────────────────┘

Monitoring

Health Check

curl http://localhost:3000/health
# Returns database and service health status

Docker Health Status

docker-compose ps
# Shows health status for each service
# HEALTHY = all dependencies responding

Logs

# All services
docker-compose logs -f

# Specific service
docker-compose logs -f api
docker-compose logs -f postgres
docker-compose logs -f redis

# Last N lines
docker-compose logs --tail=100

Troubleshooting

"Cannot connect to database"

  • Check PostgreSQL is running: docker-compose ps
  • Check DATABASE_URL is correct in .env
  • Wait 30s for database to initialize

"Redis connection refused"

  • Check Redis is running: docker-compose ps
  • Check REDIS_URL is correct in .env

"Rate limit exceeded"

  • Account is over 100 requests/minute
  • Check X-RateLimit-Reset header for next available time

"Email not found"

  • Email ID doesn't exist
  • Email belongs to different account
  • Check email was successfully sent (got 202 response)

"Invalid email format"

  • Check 'to' field is valid email address
  • Check 'subject' and 'html' are not empty

Production Checklist

  • Set RESEND_API_KEY environment variable
  • Update DATABASE_URL to production database
  • Update REDIS_URL to production Redis
  • Update webhook_url in account registration
  • Test health endpoint responds 200
  • Monitor logs for errors
  • Set NODE_ENV=production
  • Configure auto-restart policies
  • Setup log aggregation
  • Setup monitoring/alerting

Rate Limits

  • Default: 100 requests/minute per account
  • Header: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • Retry: Use Retry-After header when 429 received

For detailed documentation, see IMPLEMENTATION_SUMMARY.md