Rust para backend: axum y tokio en servicios reales
Actualizado: 2026-05-03
Durante años Rust se posicionó como lenguaje para sistemas — sistemas operativos, drivers, motores de base de datos. Esa percepción ya está desfasada. Con tokio[1] como runtime asíncrono y axum[2] como framework HTTP, escribir servicios web en Rust es razonablemente cómodo, y la diferencia de rendimiento frente a Go o Node.js es real.
Puntos clave
- El stack tokio + axum + serde + sqlx es la combinación dominante para backend en Rust: rendimiento, seguridad de tipos y ergonomía razonables una vez superada la curva inicial.
- La curva de aprendizaje es real: ownership, lifetimes y async en Rust desconciertan durante los primeros meses.
- Rust gana claramente en gateways, proxies y procesadores de eventos; para CRUD típico, Go o Node.js son más productivos.
- Herramientas como
anyhow,tracing,moldycargo nextestreducen significativamente la fricción de desarrollo. - La compilación lenta es el mayor inconveniente operativo: un proyecto medio compila en 30-60s en debug.
El stack actual
Cuando se habla de “backend en Rust”, casi siempre se refiere a esta combinación:
- tokio: el runtime async dominante. Maneja un número alto de tareas concurrentes con un número pequeño de hilos del SO usando un scheduler work-stealing.
- axum: framework HTTP construido sobre tower[3] y hyper[4]. Mantenido por el equipo de tokio.
- serde: el estándar de facto para serializar y deserializar JSON, YAML, TOML y otros formatos.
- sqlx o diesel: para acceso a base de datos. sqlx, con verificación en tiempo de compilación de queries SQL, es la opción que más crece.
Combinado, este stack permite construir un microservicio JSON sobre Postgres en cantidad similar de código a un equivalente Go, con mejor seguridad de tipos y errores más informativos en compilación.
Un endpoint mínimo en axum
use axum::{routing::get, Router, Json};
use serde::Serialize;
#[derive(Serialize)]
struct Health { status: &'static str }
async fn health() -> Json<Health> {
Json(Health { status: "ok" })
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/health", get(health));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}Tres conceptos clave en este ejemplo:
#[tokio::main]arranca el runtime async para que elmainsea una funciónasync.- Los handlers son funciones
asyncnormales — axum se encarga del binding entre tipos y respuestas HTTP. Json<T>es un extractor: como tipo de retorno, axum serializa automáticamente a JSON con el header correcto.
Por qué la curva de aprendizaje pesa
No conviene minimizarlo: Rust tiene una curva de aprendizaje real. Para alguien que viene de Go o TypeScript, los primeros meses son difíciles. Los puntos de fricción principales:
- Ownership y lifetimes. Rust no recolecta basura — el compilador valida que cada referencia sea válida. Conceptos nuevos para la mayoría de desarrolladores.
- Async en Rust. Los
Futureson lazy y dependen del runtime. Errores de compilación con tiposPin<Box<dyn Future...>>desconciertan al principio. - Errores explícitos. Sin excepciones: cada función fallible devuelve
Result<T, E>y se propaga con?. Requiere cambio de mentalidad. - Compilación lenta. Un proyecto medio compila en 30-60 segundos en debug y varios minutos en release. Ralentiza el ciclo iterativo.
Para superar esto conviene aceptar 2-3 meses de productividad reducida. Equipos sin esa paciencia suelen abandonar antes de ver los beneficios.
Comparativa pragmática
| Aspecto | Rust + axum | Go + net/http | Node + Express |
|---|---|---|---|
| Latencia p99 baja | Excelente | Muy buena | Aceptable |
| Uso de memoria | Muy bajo | Bajo | Medio-alto |
| Curva de aprendizaje | Empinada | Suave | Fácil |
| Velocidad de compilación | Lenta | Rápida | N/A (interpretado) |
| Ecosistema HTTP | Maduro y creciendo | Muy maduro | Amplísimo |
| Productividad para CRUD | Media | Alta | Muy alta |
Rust gana donde el rendimiento por nodo importa de verdad: servicios bajo carga sostenida, consumo de mensajes de Kafka a tasas altas, gateways que multiplexan miles de conexiones. En servicios CRUD típicos donde el cuello de botella es la base de datos, la ganancia es marginal y el coste de aprendizaje no compensa.
Casos donde compensa
Cuatro tipos de servicios donde Rust se adopta con éxito en producción:
- Gateways y proxies. Manejar 50.000 conexiones simultáneas con uso de memoria predecible.
- Procesadores de eventos. Kafka consumers con throughput alto donde Go ya empezaba a notar la presión del GC.
- Servicios CPU-bound. Procesamiento de imágenes, parsing de logs voluminosos, transformaciones complejas.
- Componentes embebidos en otros lenguajes. Compilar a librería estática consumida desde Python o Node.js vía FFI.
Para el típico “validar JSON, hacer 3 queries a Postgres, devolver respuesta”, Go o Node.js siguen siendo opciones más productivas.
Patrones que ahorran sufrimiento
Cinco consejos para equipos que adoptan Rust en backend:
anyhowpara errores en aplicación,thiserrorpara errores de librería. Hace la propagación de errores razonable sin boilerplate.tracingpara logs estructurados. Funciona bien con OpenTelemetry y se integra con axum via middleware.towermiddleware. Composición uniforme para logging, autenticación y rate limiting reutilizables.- Build incremental con
moldcomo linker. Reduce tiempos de iteración significativamente en Linux. cargo nextest. Un test runner mucho más rápido quecargo testpor defecto.
Para la capa de caché sobre Postgres en servicios axum, Redis con estrategias cache-aside es la combinación más habitual. El stack de observabilidad con Grafana integra bien con tracing vía OTLP. Los contenedores de servicios Rust son más fáciles de operar con Podman en entornos rootless.
Conclusión
Rust en backend es una opción seria, no una promesa futura. La combinación tokio + axum es estable, performante y razonablemente productiva una vez superada la curva inicial. Pero no es la respuesta correcta para cualquier servicio — es una elección con trade-offs claros que conviene hacer conscientemente, basándose en cargas de trabajo reales y no en hype.