DEV Community

Cover image for Cómo probar la API de Rust
Roobia
Roobia

Posted on • Originally published at apidog.com

Cómo probar la API de Rust

Rust te da un servidor HTTP rápido y con seguridad de tipos en pocas cientos de líneas. Lo que no te da por defecto es un ciclo de feedback rápido para validar la API como la consumen otros clientes: estado HTTP, JSON serializado, headers, autenticación, errores y contratos compartidos.

Prueba Apidog hoy

En esta guía vas a montar un flujo de pruebas de API para Rust con Apidog: conectar un servidor Axum o Actix, crear requests reutilizables, validar JSON de Serde, manejar JWT, simular endpoints para frontend y ejecutar escenarios en CI.

Si vienes de Postman o curl, Apidog también te permite trabajar diseño-primero: generar OpenAPI desde solicitudes guardadas, publicar mocks y compartir entornos de equipo. La migración desde Postman queda para otra lectura; aquí nos enfocamos en Rust.

TL;DR

  • Ejecuta tu servidor Rust localmente con cargo run.
  • Configura http://localhost:3000 como baseUrl en Apidog.
  • Guarda secretos como variables de entorno.
  • Crea una primera request GET /healthz para validar conectividad.
  • Para JSON, prueba tus structs de Serde con cuerpos reales y scripts de aserción.
  • Para JWT, genera el token una vez en un script pre-request y reutilízalo como {{token}}.
  • Usa mocks de Apidog para que frontend avance antes de que el handler esté listo.
  • Guarda todo como un Escenario de Prueba y ejecútalo en CI con apidog-cli.

Por qué probar una API de Rust fuera de la cadena de herramientas de Rust

cargo test es necesario, pero no sustituye una prueba real sobre HTTP. Para validar códigos de estado, headers, errores de serialización y contratos JSON, normalmente terminas escribiendo pruebas de integración específicas por endpoint.

Apidog añade una capa de contrato sobre el servidor en ejecución:

  • La request vive una vez.
  • Las aserciones viven junto a la request.
  • El frontend puede abrir el mismo proyecto.
  • Los mocks salen del mismo contrato.
  • CI puede ejecutar las mismas pruebas contra el binario real.

Tres motivos prácticos para añadirlo al flujo de Rust:

  1. Las verificaciones de contrato se desacoplan de la compilación.

    Apidog prueba un servidor en ejecución. No necesitas esperar a rustc para comprobar si /users sigue devolviendo 200.

  2. Los mocks son compartibles.

    El frontend recibe una URL estable con JSON válido, incluso si el handler todavía no existe.

  3. OpenAPI sale de las requests guardadas.

    Puedes exportar OpenAPI 3.1 sin anotar cada ruta con utoipa, aide u otra librería.

Paso 1: Añade tu servidor Rust como entorno de Apidog

Arranca una API mínima con Axum:

use axum::{routing::get, Router};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/healthz", get(|| async { "ok" }));

    let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();

    axum::serve(listener, app).await.unwrap();
}
Enter fullscreen mode Exit fullscreen mode

En Apidog:

  1. Crea un proyecto nuevo.
  2. Abre Gestión de Entornos.
  3. Añade un entorno llamado Rust Local.
Variable Valor
baseUrl http://localhost:3000
token déjalo vacío por ahora
apiVersion v1

Añade también un entorno Rust Staging con la URL desplegada. Así puedes cambiar de local a staging desde el selector de entorno sin editar requests.

Paso 2: Crea la primera request

Dentro del proyecto, crea una carpeta llamada Rust API.

Añade una request:

  • Método: GET
  • URL: {{baseUrl}}/healthz

Envía la solicitud. Si el servidor está corriendo, deberías recibir:

200 OK
ok
Enter fullscreen mode Exit fullscreen mode

Guárdala como health-check.

Si recibes connection refused, revisa:

  • Que el proceso siga ejecutándose.
  • Que el puerto sea 3000.
  • Que el servidor escuche en 0.0.0.0:3000.

Para desarrollo local con herramientas externas o Docker, 0.0.0.0 suele evitar problemas de resolución entre interfaces. Si usas 127.0.0.1, algunas llamadas desde contenedores o procesos externos pueden no llegar al servidor esperado.

Paso 3: Prueba requests y respuestas JSON con Serde

Una API Rust típica recibe JSON, deserializa con Serde y devuelve JSON serializado.

Añade un endpoint POST /users:

use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
    Json(User {
        id: 1,
        name: payload.name,
        email: payload.email,
    })
}

let app = Router::new().route("/users", post(create_user));
Enter fullscreen mode Exit fullscreen mode

En Apidog, crea una request:

  • Método: POST
  • URL: {{baseUrl}}/users
  • Body: JSON
{
  "name": "Ada Lovelace",
  "email": "ada@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Envía la request y guárdala como create-user.

Ahora añade aserciones en la pestaña Pruebas:

pm.test("Status is 200", () => {
  pm.expect(pm.response.code).to.eql(200);
});

pm.test("Body has id, name, email", () => {
  const body = pm.response.json();

  pm.expect(body).to.have.property("id");
  pm.expect(body.name).to.eql("Ada Lovelace");
  pm.expect(body.email).to.match(/^[^@]+@[^@]+$/);
});
Enter fullscreen mode Exit fullscreen mode

Con esto conviertes la forma pública del JSON en un contrato ejecutable. Si más adelante cambias atributos de Serde, nombres de campos o tipos, la prueba falla antes de que el cambio llegue a producción.

Paso 4: Cubre rechazos de Serde

No pruebes solo el caso feliz. En Rust, gran parte del contrato real aparece cuando Serde rechaza una entrada.

Crea estas requests:

Request Body Esperado
create-user-missing-email { "name": "Ada" } 422, el cuerpo menciona missing field email
create-user-extra-field { "name": "Ada", "email": "a@b.c", "admin": true } 200 si no usas #[serde(deny_unknown_fields)]; 422 si lo usas
create-user-wrong-type { "name": 1, "email": "a@b.c" } 422, menciona invalid type: integer

Ejemplo de aserción:

pm.test("Status is 422", () => {
  pm.expect(pm.response.code).to.eql(422);
});

pm.test("Error mentions missing email", () => {
  pm.expect(pm.response.text()).to.include("missing field");
});
Enter fullscreen mode Exit fullscreen mode

Esto documenta tu política real de validación. Si activas deny_unknown_fields más adelante, la prueba correspondiente se pondrá en rojo y te avisará de que cambiaste el contrato público.

Paso 5: Prueba rutas protegidas con JWT

Muchas APIs Rust usan middleware o extractores para proteger handlers. Un patrón común con Axum es validar un JWT desde cookies o headers.

Ejemplo simplificado:

use axum::{
    extract::Json,
    http::StatusCode,
};
use axum_extra::extract::cookie::PrivateCookieJar;
use jsonwebtoken::{decode, DecodingKey, Validation};

async fn me(jar: PrivateCookieJar) -> Result<Json<User>, StatusCode> {
    let token = jar
        .get("token")
        .ok_or(StatusCode::UNAUTHORIZED)?;

    let claims = decode::<Claims>(
        token.value(),
        &DecodingKey::from_secret(b"secret"),
        &Validation::default(),
    )
    .map_err(|_| StatusCode::UNAUTHORIZED)?;

    Ok(Json(User {
        id: claims.claims.sub,
        name: "Ada".into(),
        email: "ada@example.com".into(),
    }))
}
Enter fullscreen mode Exit fullscreen mode

En Apidog, evita generar JWT manualmente en cada request. Añade un Script Pre-Solicitud a nivel de carpeta:

const jwt = require("jsonwebtoken");

const token = jwt.sign(
  {
    sub: 1,
    exp: Math.floor(Date.now() / 1000) + 3600,
  },
  "secret"
);

pm.environment.set("token", token);
Enter fullscreen mode Exit fullscreen mode

Luego configura la carpeta:

  • Auth: Bearer Token
  • Token: {{token}}

Ahora todas las requests dentro de la carpeta heredan el JWT.

Para más detalle, consulta cómo probar autenticación JWT en APIs.

Paso 6: Prueba streaming y Server-Sent Events

Rust tiene buen soporte para streaming. En Axum, una respuesta Sse envuelve un futures::Stream y emite text/event-stream.

El formato típico es:

data: { ... }

data: { ... }

event: done
data: {}
Enter fullscreen mode Exit fullscreen mode

En Apidog, la request se crea como cualquier GET. Cuando el Content-Type es text/event-stream, el panel de respuesta cambia a modo streaming y muestra los eventos a medida que llegan.

Valida al menos:

  • Que el primer fragmento llegue dentro del SLA esperado.
  • Que se emita un evento final, por ejemplo event: done.
  • Que el stream no quede abierto para siempre.
  • Que el timeout de la request sea estricto.

Si usas WebSockets en lugar de SSE, Apidog tiene un tipo de request específico para WebSocket. El flujo es equivalente: crear conexión, guardar mensajes esperados y afirmar respuestas.

Paso 7: Simula la API para desarrollo frontend en paralelo

El frontend no debería bloquearse porque el handler Rust todavía no compila o aún no existe.

Con Apidog puedes publicar un mock basado en la misma request create-user.

Pasos:

  1. Haz clic derecho en create-user.
  2. Elige Smart Mock.
  3. Actívalo.

Apidog servirá una respuesta sintética en una URL similar a:

https://mock.apidog.com/m1/<projectId>/users
Enter fullscreen mode Exit fullscreen mode

El mock acepta el mismo body que tu API real. El frontend puede hacer POST contra esa URL mientras tú terminas el handler.

Para mocks dinámicos, usa Advanced Mock:

return {
  id: Math.floor(Math.random() * 10000),
  name: body.name,
  email: body.email,
  createdAt: new Date().toISOString()
};
Enter fullscreen mode Exit fullscreen mode

Cuando el handler Rust esté listo, el frontend cambia la URL base de nuevo a:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

El contrato no cambia.

Este patrón también aplica a otros runtimes. El equipo cubre flujos similares en cómo construir y probar una API de Spring Boot y en el flujo general de pruebas de API.

Paso 8: Guarda las requests como escenario de prueba de CI

Un Escenario de Prueba de Apidog encadena requests, comparte variables y se puede ejecutar sin interfaz gráfica.

Crea un escenario con este orden:

  1. health-check

    • Espera 200.
  2. create-user

    • Espera 200.
    • Captura body.id en una variable.
  3. create-user-missing-email

    • Espera 422.
  4. me

    • Usa el pre-request JWT.
    • Espera 200.
    • Valida que el id devuelto coincide con el capturado.
  5. Request SSE

    • Valida que el stream completa en menos de 5 segundos.

Exporta el escenario como JSON y guárdalo en el repositorio:

tests/apidog/contract.json
Enter fullscreen mode Exit fullscreen mode

Ejemplo de ejecución en CI:

- name: Run API contract tests
  run: |
    cargo build --release
    ./target/release/myserver &
    sleep 2
    apidog-cli run tests/apidog/contract.json --env "Rust Local"
Enter fullscreen mode Exit fullscreen mode

Con esto, cada PR que modifique un handler se prueba contra un binario Rust real. Si cambia un nombre de campo de Serde, un código de estado o una regla de JWT, CI lo detecta antes del merge.

Paso 9: Genera OpenAPI desde las requests guardadas

Cuando el conjunto de requests sea estable:

  1. Abre el menú de exportación de Apidog.
  2. Elige OpenAPI 3.1.
  3. Exporta el documento.

Obtendrás una especificación basada en las requests reales y sus ejemplos.

Puedes usarla para generar clientes tipados en:

  • TypeScript
  • Swift
  • Kotlin
  • Python
  • otros lenguajes compatibles con OpenAPI

Si quieres versionarla dentro del repositorio Rust, ejecútalo desde CI con apidog-cli export y escribe el resultado en:

openapi.json
Enter fullscreen mode Exit fullscreen mode

Así el contrato queda junto al código, pero sigue validándose desde fuera de cargo.

Preguntas frecuentes

¿Funciona Apidog con Axum y Actix-web?

Sí. Apidog habla HTTP, no Rust. Funciona con Axum, Actix-web, Rocket, Warp, Poem, Loco o cualquier servidor que responda requests HTTP.

La consideración práctica para desarrollo local es enlazar a 0.0.0.0 si necesitas que herramientas externas o contenedores lleguen al proceso.

¿Cómo pruebo handlers que hacen panic?

Ejecuta el servidor con CatchPanicLayer de tower-http delante del router. Así un panic puede convertirse en 500 con una respuesta controlada.

Luego crea una request en Apidog que active esa ruta y afirma:

pm.test("Status is 500", () => {
  pm.expect(pm.response.code).to.eql(500);
});
Enter fullscreen mode Exit fullscreen mode

Si no capturas el panic, la conexión puede cerrarse y Apidog mostrará un error de red. Eso también describe un comportamiento observable del contrato.

¿Puedo ejecutar Apidog contra un binario Rust en Docker?

Sí. Apunta baseUrl al puerto expuesto del contenedor.

Si usas Docker Compose, asegúrate de que el runner de Apidog esté en la misma red o usa el puerto publicado en el host.

¿Qué pasa con gRPC?

Apidog incluye requests gRPC. Puedes importar tus archivos .proto, elegir servicio y método, rellenar el payload y enviar la llamada.

Los conceptos de entorno, autenticación y escenarios de prueba se mantienen igual.

¿El escenario de prueba reemplaza a cargo test?

No.

Usa ambos:

  • cargo test para lógica interna, unidades y comportamiento de dominio.
  • Apidog para validar la superficie HTTP real: JSON, headers, auth, códigos de estado, CORS, errores y contratos.

Detectan problemas distintos.

¿Apidog es gratuito para proyectos Rust open source?

Sí. El cliente de Apidog es gratuito para individuos y equipos pequeños. Los escenarios de prueba, mocks y exportación OpenAPI forman parte del nivel gratuito.

Si mantienes una API pública de Rust, puedes incluir el proyecto o los escenarios en tu repositorio para que otros desarrolladores ejecuten el mismo contrato.

Conclusión

Una API Rust necesita más que compilación correcta. Necesita un contrato HTTP ejecutable.

Con Apidog puedes crear requests reales, añadir aserciones, generar mocks para frontend, exportar OpenAPI y correr el mismo flujo en CI contra el binario Rust en vivo.

Empieza con GET /healthz, añade tus endpoints JSON, cubre errores de Serde, configura JWT y exporta un escenario de prueba. El resultado es un ciclo de feedback más rápido, desacoplado de cargo, y un contrato compartido por backend, frontend y CI.

Descarga Apidog y apúntalo a tu servidor Rust. La configuración inicial lleva pocos minutos y te deja una suite de contrato reutilizable para cada cambio futuro.

Top comments (0)