Writing Web Services in Rust
Rust Rust — memory-safe systems programming language rust-lang.org ↗
is not the obvious choice for web services. The learning curve is steep, compile times are longer than Go or TypeScript, and the ecosystem is younger than Java or Python. But for services where performance, memory safety, and predictable latency matter — high-throughput API gateways, real-time data processors, financial transaction handlers — Rust delivers results that are difficult to achieve in garbage-collected languages. A well-written Rust service handles tens of thousands of concurrent connections with single-digit millisecond p99 latency and a memory footprint measured in megabytes.
The two dominant web frameworks are
Actix Web Actix Web — powerful Rust web framework actix.rs ↗
and Axum Axum — ergonomic Rust web framework built on Tokio github.com/tokio-rs/axum ↗
. Actix Web is the more established option, with a comprehensive feature set and strong benchmark numbers. Axum, built on top of the Tower middleware ecosystem and maintained by the Tokio team, takes a more modular approach — its handler functions are plain async functions that use extractors to parse requests, making them easy to test in isolation. Both frameworks use
Tokio Tokio — async runtime for Rust tokio.rs ↗
as the async runtime, so the performance characteristics are nearly identical. I lean toward Axum for new projects because its composability with Tower services means middleware like rate limiting, tracing, and authentication can be shared across HTTP and gRPC services.
Database access in Rust Rust — memory-safe systems programming language rust-lang.org ↗
typically means
SQLx SQLx — async SQL toolkit for Rust with compile-time checked queries github.com/launchbadge/sqlx ↗
or Diesel for
PostgreSQL PostgreSQL — powerful open-source relational database postgresql.org ↗
. SQLx is the async-native option: it checks your SQL queries against the actual database schema at compile time, catching typos, type mismatches, and missing columns before your code runs. This compile-time verification is unique to Rust’s ecosystem and eliminates an entire class of runtime errors. For
Redis Redis — in-memory data store for caching and messaging redis.io ↗
, the
redis crate with the tokio-comp feature provides an async connection manager that integrates cleanly with the Tokio Tokio — async runtime for Rust tokio.rs ↗
runtime. Connection pooling through
deadpool or bb8 ensures your service reuses connections efficiently under load.
Deploying a Rust Rust — memory-safe systems programming language rust-lang.org ↗
service follows the same multi-stage
Docker Docker — platform for building and running containers docker.com ↗
build pattern as Go: compile in a builder stage with the full Rust toolchain, copy the binary into a minimal runtime image. The resulting container is typically 10-20 MB, starts in under a second, and uses minimal memory at idle. For
Kubernetes Kubernetes — container orchestration at scale kubernetes.io ↗
deployments, Rust’s low resource consumption means you can pack more replicas per node than equivalent services in other languages, which directly reduces infrastructure costs for high-scale workloads.
The trade-off is development velocity. The borrow checker, lifetime annotations, and strict type system mean that code that would compile immediately in Go or TypeScript might require 30 minutes of wrestling with the compiler in Rust. But here is the counterpoint: the time you spend satisfying the compiler is time you do not spend debugging race conditions, null pointer exceptions, or memory leaks in production. For services that run for months without restarts and handle millions of requests, that trade-off is worth it. Start with your most performance-critical service, prove the value, and expand from there — converting an entire backend to Rust at once is rarely the right move.