A production-ready newsletter service built with Rust, featuring subscription management, authentication, newsletter publishing with idempotency, background email delivery, and PostgreSQL integration.
Version: 0.16.0
- RESTful API - Health check, subscription, authentication, and admin endpoints
- PostgreSQL Integration - Type-safe database queries with sqlx
- Redis Session Store - Fast, distributed session management with Redis backend
- Structured Logging - JSON-formatted tracing with Bunyan
- Configuration Management - YAML-based settings with environment overrides
- Email Confirmation - Two-step subscription process with email verification
- Token Management - Secure subscription token generation and validation
- Authentication & Authorization - Session-based auth with password hashing (Argon2)
- Admin Dashboard - Protected admin interface for newsletter management
- Newsletter Publishing - Idempotent newsletter creation and delivery
- Background Worker - Asynchronous email delivery queue with retry logic
- Containerized - Docker/Podman support with multi-stage builds
- Database Migrations - Automated schema management
- Rust 1.92+
- PostgreSQL 15+
- Podman | Docker
- Redis 7
./scripts/init_db.sh./scripts/init_redis.shcargo runcargo testpodman build -t zero2prod:latest .
podman run --rm -p 8000:8000 zero2prod:latest| Method | Path | Description |
|---|---|---|
| GET | / |
Home page |
| GET | /health_check |
Health monitoring endpoint |
| POST | /subscriptions |
Newsletter subscription (form data: name, email) |
| GET | /subscriptions/confirm |
Email confirmation endpoint (query param: subscription_token) |
| GET | /login |
Login form |
| POST | /login |
Login submission (form data: username, password) |
| Method | Path | Description |
|---|---|---|
| GET | /admin/dashboard |
Admin dashboard |
| GET | /admin/newsletters |
Newsletter publishing form |
| POST | /admin/newsletters |
Publish newsletter (JSON: title, text_content, html_content, idempotency_key) |
| GET | /admin/password |
Change password form |
| POST | /admin/password |
Change password submission |
| POST | /admin/logout |
Logout |
zero2prod/
├── src/
│ ├── main.rs # Application entry point
│ ├── lib.rs # Library exports
│ ├── configuration.rs # Settings & database config
│ ├── startup.rs # HTTP server setup
│ ├── telemetry.rs # Logging configuration
│ ├── email_client.rs # Email service integration
│ ├── session_state.rs # Session management
│ ├── utils.rs # Utility functions
│ ├── issue_delivery_worker.rs # Background email delivery worker
│ ├── authentication/ # Authentication & authorization
│ │ ├── mod.rs
│ │ ├── middleware.rs # Auth middleware
│ │ └── password.rs # Password hashing/verification
│ ├── domain/ # Domain models and validation
│ │ ├── mod.rs
│ │ ├── new_subscriber.rs
│ │ ├── subscriber_email.rs
│ │ └── subscriber_name.rs
│ ├── idempotency/ # Idempotency key management
│ │ ├── mod.rs
│ │ ├── key.rs
│ │ └── persistence.rs
│ └── routes/
│ ├── mod.rs
│ ├── health_check.rs # Health endpoint
│ ├── home/ # Home page
│ ├── login/ # Login endpoints
│ │ ├── get.rs
│ │ └── post.rs
│ ├── subscriptions.rs # Subscription endpoint
│ ├── subscriptions_confirm.rs # Email confirmation
│ └── admin/ # Protected admin routes
│ ├── dashboard.rs
│ ├── logout.rs
│ ├── newsletter/ # Newsletter publishing
│ │ ├── get.rs
│ │ └── post.rs
│ └── password/ # Password management
│ ├── get.rs
│ └── post.rs
├── tests/
│ └── api/ # Integration tests
│ ├── main.rs
│ ├── helpers.rs
│ ├── health_check.rs
│ ├── subscriptions.rs
│ ├── subscriptions_confirm.rs
│ ├── login.rs
│ ├── admin_dashboard.rs
│ ├── change_password.rs
│ └── newsletter.rs
├── migrations/ # Database schema
│ ├── *_create_subscriptions_table.sql
│ ├── *_create_subscription_tokens_table.sql
│ ├── *_create_users_table.sql
│ ├── *_create_idempotency_table.sql
│ ├── *_create_newsletter_issue_table.sql
│ └── *_create_issue_delivery_queue_table.sql
├── scripts/
│ ├── init_db.sh # Database initialization
│ └── release-and-build.sh # Release automation
├── configuration/ # YAML config files
└── Dockerfile # Container definition
main -> telemetry -> configuration -> startup -> routes
|
PgPool (database)
|
EmailClient
|
SessionStore (Redis-backed sessions)
|
HttpServer (actix-web) -> routes (public + admin)
|
Authentication Middleware -> admin routes
Background Worker:
issue_delivery_worker -> PgPool + EmailClient -> process delivery queue
- main → Load configuration → Initialize telemetry → Connect to database
- startup → Create HTTP server → Register routes → Setup session middleware
- routes → Handle requests → Database operations → Response
- Request → Session middleware → Check authentication
- If authenticated → Route handler → Database operations → Response
- If not authenticated → Redirect to
/login
- Admin submits newsletter (with idempotency key)
- Check idempotency table (prevent duplicates)
- Insert newsletter into
newsletter_issuestable - Queue delivery tasks in
issue_delivery_queue(one per confirmed subscriber) - Return success response
- Background worker processes queue asynchronously
- Worker polls
issue_delivery_queuetable - Dequeue task with
FOR UPDATE SKIP LOCKED(prevents race conditions) - Send email via EmailClient
- Delete task from queue on success
- Repeat until queue is empty
- Web Framework: actix-web 4.x
- Database: PostgreSQL with sqlx (compile-time verified queries)
- Async Runtime: tokio
- Logging: tracing + tracing-bunyan-formatter
- Configuration: config crate with YAML
- Security:
- secrecy for sensitive data
- argon2 for password hashing
- actix-session for session management
- Testing: wiremock for HTTP mocking
- subscriptions - Subscriber information with confirmation status
- subscription_tokens - Email confirmation tokens
- users - Admin user credentials (hashed passwords)
- newsletter_issues - Published newsletters
- issue_delivery_queue - Pending email delivery tasks
- idempotency - Idempotency key tracking for duplicate prevention
./scripts/release-and-build.shThis script:
- Bumps version with cargo-release
- Builds debug binary
- Creates container image with version tag
- Tags as
latest
Edit configuration.yaml for database and application settings.
APP_ENVIRONMENT- Set toproductionordevelopmentDATABASE_URL- PostgreSQL connection string (optional)
Integration tests use isolated PostgreSQL databases with unique names per test run. Tests cover:
- Health checks
- Subscription flow (create + confirm)
- Authentication (login/logout)
- Admin dashboard access
- Password changes
- Newsletter publishing (including idempotency and concurrency)
cargo test # Run all tests
cargo test --lib # Run unit tests only (15 tests)
cargo test --test api # Run integration tests only
cargo nextest run # Parallel test execution- Password Security: Argon2id hashing with PHC string format
- Session Management: Secure session cookies with Redis backend
- SQL Injection Prevention: Parameterized queries via sqlx
- CSRF Protection: Session-based authentication
- Idempotency: Prevents duplicate newsletter sends
- Database Locking:
FOR UPDATE SKIP LOCKEDprevents race conditions
Last updated: 2026-01-15