Zero To Production is the ideal starting point for your journey as a Rust backend developer.
You will learn by doing: you will build a fully functional email newsletter API, starting from scratch.
You'll learn how to:
* Navigate and leverage Rust's crates ecosystem
* Structure your application to make it modular and extensible
* Write tests, from single units to full-blown integration tests
* Enforce your domain invariants using Rust's type system
* Authenticate and authorize users of your API
* Implement a robust error handling strategy
* Observe the state of your application using structured logs
* Set up an extensive continuous integration and continuous deployment pipeline for your Rust projects
Author(s): Luca Palmieri
Edition: 20210930
Publisher: Independently Published
Year: 2021
Language: English
Pages: 313
Foreword
What Is This Book About
Cloud-native applications
Working in a team
Who Is This Book For
Getting Started
Installing The Rust Toolchain
Compilation Targets
Release Channels
What Toolchains Do We Need?
Project Setup
IDEs
Rust-analyzer
IntelliJ Rust
What Should I Use?
Continuous Integration
CI Steps
Tests
Code Coverage
Linting
Formatting
Security Vulnerabilities
Ready-to-go CI Pipelines
Building An Email Newsletter
Our Driving Example
Problem-based Learning
Course-correcting
What Should Our Newsletter Do?
Capturing Requirements: User Stories
Working In Iterations
Coming Up
Sign Up A New Subscriber
Our Strategy
Choosing A Web Framework
Our First Endpoint: A Basic Health Check
Wiring Up actix-web
Anatomy Of An actix-web Application
Server - HttpServer
Application - App
Endpoint - Route
Runtime - actix_rt
Implementing The Health Check Handler
Our First Integration Test
How Do You Test An Endpoint?
Where Should I Put My Tests?
Changing Our Project Structure For Easier Testing
Implementing Our First Integration Test
Polishing
Clean Up
Choosing A Random Port
Refocus
Working With HTML Forms
Refining Our Requirements
Capturing Our Requirements As Tests
Parsing Form Data From A POST Request
Extractors
Form And FromRequest
Serialisation In Rust: serde
Putting Everything Together
Storing Data: Databases
Choosing A Database
Choosing A Database Crate
Compile-time Safety
Query Interface
Async Support
Summary
Our Pick: sqlx
Integration Testing With Side-effects
Database Setup
Docker
Database Migrations
Writing Our First Query
Sqlx Feature Flags
Configuration Management
Connecting To Postgres
Our Test Assertion
Updating Our CI Pipeline
Application State In actix-web
actix-web Workers
The Data Extractor
The INSERT Query
Updating Our Tests
Test Isolation
Summary
Telemetry
Unknown Unknowns
Observability
Logging
The log Crate
actix-web's Logger Middleware
The Facade Pattern
Instrumenting POST /subscriptions
Interactions With External Systems
Think Like A User
Logs Must Be Easy To Correlate
Structured Logging
The tracing Crate
Migrating From log To tracing
tracing's Span
tracing-futures
tracing's Subscriber
tracing-subscriber
tracing-bunyan-formatter
tracing-log
Removing Unused Dependencies
Cleaning Up Initialisation
Logs For Integration Tests
Cleaning Up Instrumentation Code - tracing::instrument
Request Id
Leveraging The tracing Ecosystem
Summary
Going Live
We Must Talk About Deployments
Choosing Our Tools
Virtualisation: Docker
Hosting: DigitalOcean
A Dockerfile For Our Application
Dockerfiles
Build Context
Sqlx Offline Mode
Running An Image
Networking
Hierarchical Configuration
Database Connectivity
Optimising Our Docker Image
Docker Image Size
Caching For Rust Docker Builds
Deploy To DigitalOcean Apps Platform
Setup
App Specification
How To Inject Secrets Using Environment Variables
Connecting To Digital Ocean's Postgres Instance
Environment Variables In The App Spec
One Last Push
Reject Invalid Subscribers #1
Requirements
Domain Constraints
Security Constraints
First Implementation
Validation Is A Leaky Cauldron
Type-Driven Development
Ownership Meets Invariants
AsRef
Panics
Error As Values - Result
Converting parse To Return Result
Insightful Assertion Errors: claim
Unit Tests
Handling A Result
match
The ? Operator
400 Bad Request
The Email Format
The SubscriberEmail Type
Breaking The Domain Sub-Module
Skeleton Of A New Type
Property-based Testing
How To Generate Random Test Data With fake
quickcheck Vs proptest
Getting Started With quickcheck
Implementing The Arbitrary Trait
Payload Validation
Refactoring With TryFrom
Summary
Reject Invalid Subscribers #2
Confirmation Emails
Subscriber Consent
The Confirmation User Journey
The Implementation Strategy
EmailClient, Our Email Delivery Component
How To Send An Email
Choosing An Email API
The Email Client Interface
How To Write A REST Client Using reqwest
reqwest::Client
Connection Pooling
How To Reuse The Same reqwest::Client In actix-web
Configuring Our EmailClient
How To Test A REST Client
HTTP Mocking With wiremock
wiremock::MockServer
wiremock::Mock
The Intent Of A Test Should Be Clear
Mock expectations
First Sketch Of EmailClient::send_email
reqwest::Client::post
JSON body
Authorization Token
Executing The Request
Tightening Our Happy Path Test
Refactoring: Avoid Unnecessary Memory Allocations
Dealing With Failures
Error Status Codes
Timeouts
Refactoring: Test Helpers
Refactoring: Fail fast
Why Do We Write Tests?
Why Don't We Write Tests?
Test Code Is Still Code
Our Test Suite
Test Discovery
One Test File, One Crate
Sharing Test Helpers
Sharing Startup Logic
Extracting Our Startup Code
Testing Hooks In Our Startup Logic
Build An API Client
Summary
Refocus
Zero Downtime Deployments
Reliability
Deployment Strategies
Naive Deployment
Load Balancers
Rolling Update Deployments
Digital Ocean App Platform
Database Migrations
State Is Kept Outside The Application
Deployments And Migrations
Multi-step Migrations
A New Mandatory Column
Step 1: Add As Optional
Step 2: Start Using The New Column
Step 3: Backfill And Mark As NOT NULL
A New Table
Sending A Confirmation Email
A Static Email
Red test
Green test
A Static Confirmation Link
Red Test
Refactor
Pending Confirmation
Red test
Green Test
Skeleton of GET /subscriptions/confirm
Red Test
Green Test
Connecting The Dots
Red Test
Green Test
Refactor
Subscription Tokens
Red Test
Green Test
Database Transactions
All Or Nothing
Transactions In Postgres
Transactions In Sqlx
Summary
Error Handling
What Is The Purpose Of Errors?
Internal Errors
Enable The Caller To React
Help An Operator To Troubleshoot
Errors At The Edge
Help A User To Troubleshoot
Summary
Error Reporting For Operators
Keeping Track Of The Error Root Cause
The Error Trait
Trait Objects
Error::source
Errors For Control Flow
Layering
Modelling Errors as Enums
The Error Type Is Not Enough
Removing The Boilerplate With thiserror
Avoid ``Ball Of Mud'' Error Enums
Using anyhow As Opaque Error Type
anyhow Or thiserror?
Who Should Log Errors?
Summary
Naive Newsletter Delivery
User Stories Are Not Set In Stone
Do Not Spam Unconfirmed Subscribers
Set Up State Using The Public API
Scoped Mocks
Green Test
All Confirmed Subscribers Receive New Issues
Composing Test Helpers
Implementation Strategy
Body Schema
Test Invalid Inputs
Fetch Confirmed Subscribers List
Send Newsletter Emails
context Vs with_context
Validation Of Stored Data
Responsibility Boundaries
Follow The Compiler
Remove Some Boilerplate
Limitations Of The Naive Approach
Summary
Securing Our API
Authentication
Drawbacks
Something They Know
Something They Have
Something They Are
Multi-factor Authentication
Password-based Authentication
Basic Authentication
Extracting Credentials
Password Verification - Naive Approach
Password Storage
No Need To Store Raw Passwords
Using A Cryptographic Hash
Preimage Attack
Naive Dictionary Attack
Dictionary Attack
Argon2
Salting
PHC String Format
Do Not Block The Async Executor
Tracing Context Is Thread-Local
User Enumeration
Is it safe?
Transport Layer Security (TLS)
Password Reset
Interaction Types
Machine To Machine
Client Credentials via OAuth2
Person Via Browser
Federated Identity
Machine to machine, on behalf of a person
What Should We Do Next