\usepackage{rust}

API de LaTeX a PDF para Rust

Compile LaTeX a PDF desde Rust con la API REST de FormaTeX. reqwest asíncrono + tokio: sin instalar TeX Live y sin binarios que empaquetar.

Also available in:Python·Node.js·Go·PHP·Ruby·Rust·cURL|Full API reference

\section{Por qué Rust}

Por qué Rust + FormaTeX

Asincronía sin sobrecarga

El cliente asíncrono de reqwest encaja de forma natural con el runtime de Tokio. Compile varios documentos a la vez sin complejidad adicional.

Imágenes de Docker ligeras

Su binario de Rust se mantiene pequeño: sin TeX Live y sin apt install texlive-full. FormaTeX se encarga por completo del entorno de compilación.

Gestión de errores con tipos seguros

El tipo Result de Rust combina muy bien con la API de FormaTeX. Convierta los errores HTTP en errores de dominio a nivel de tipos, sin sorpresas en tiempo de ejecución.

\section{Dependencies}

Dependencias de Cargo.toml

Cargo.toml
[dependencies] reqwest = { version = "0.12", features = ["json"] } tokio = { version = "1", features = ["full"] } serde_json = "1" serde = { version = "1", features = ["derive"] } # For Axum integration axum = "0.7" bytes = "1"

\section{Inicio rápido}

Inicio rápido con reqwest

El siguiente ejemplo compila un documento LaTeX mínimo con reqwest y escribe el PDF resultante en disco. Lea su clave de API del entorno con std::env::var("FORMATEX_API_KEY") .

src/main.rs
use reqwest::Client;
use serde_json::json;
use std::fs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("FORMATEX_API_KEY")?;

    let latex = r#"\documentclass{article}
\usepackage{amsmath}
\begin{document}
Hello from Rust! $E = mc^2$
\end{document}"#;

    let client = Client::new();
    let response = client
        .post("https://api.formatex.io/api/v1/compile")
        .header("X-API-Key", &api_key)
        .json(&json!({ "latex": latex, "engine": "pdflatex" }))
        .send()
        .await?;

    if !response.status().is_success() {
        let body = response.text().await?;
        return Err(format!("API error: {body}").into());
    }

    let pdf = response.bytes().await?;
    fs::write("output.pdf", &pdf)?;
    println!("PDF saved to output.pdf");
    Ok(())
}

\section{Handler de Axum}

Handler de Axum

Registre un AppState compartido en su router de Axum y monte compile_handler en cualquier ruta. El handler acepta un cuerpo JSON con source y, opcionalmente, engine, y devuelve los bytes del PDF directamente al cliente.

src/handlers/compile.rs
use axum::{
    body::Bytes,
    extract::State,
    http::{HeaderMap, StatusCode},
    response::{IntoResponse, Response},
    Json,
};
use reqwest::Client;
use serde::Deserialize;

#[derive(Clone)]
pub struct AppState {
    pub http:    Client,
    pub api_key: String,
}

#[derive(Deserialize)]
pub struct CompileRequest {
    pub source: String,
    #[serde(default = "default_engine")]
    pub engine: String,
}

fn default_engine() -> String {
    "pdflatex".to_string()
}

pub async fn compile_handler(
    State(state): State<AppState>,
    Json(body):   Json<CompileRequest>,
) -> Result<Response, (StatusCode, String)> {
    let res = state
        .http
        .post("https://api.formatex.io/api/v1/compile")
        .header("X-API-Key", &state.api_key)
        .json(&serde_json::json!({
            "latex": body.source,
            "engine": body.engine,
        }))
        .send()
        .await
        .map_err(|e| (StatusCode::BAD_GATEWAY, e.to_string()))?;

    if !res.status().is_success() {
        let msg = res.text().await.unwrap_or_default();
        return Err((StatusCode::UNPROCESSABLE_ENTITY, msg));
    }

    let pdf: Bytes = res
        .bytes()
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", "application/pdf".parse().unwrap());
    headers.insert(
        "Content-Disposition",
        "attachment; filename=output.pdf".parse().unwrap(),
    );

    Ok((headers, pdf).into_response())
}

\section{Authentication}

Configuración de la variable de entorno

Guarde su clave de API en el entorno; nunca la escriba directamente en los archivos de código. Léala al iniciar y falle de inmediato si no existe:

// Fail fast at startup let api_key = std::env::var( "FORMATEX_API_KEY" ).expect( "FORMATEX_API_KEY not set" );
  • → Genere claves de API desde su panel después de registrarse.
  • → Use un archivo .env con el crate dotenvy durante el desarrollo local.
  • → Todas las solicitudes deben incluir X-API-Key: <key> .

\section{Reference}

Referencia de la API

CampoTipoObligatorioDescripción
latexstringSíEl código fuente LaTeX completo como cadena de texto
enginestringNoMotor que se usará. Valor predeterminado: "pdflatex". Opciones: pdflatex, xelatex, lualatex, latexmk

POST https://api.formatex.io/api/v1/compile — Devuelve los bytes del PDF sin procesar si tiene éxito ( 200), o un error JSON si falla (422/ 401).

\section{Gestión de errores}

Gestión de errores

Compruebe response.status().is_success() antes de leer el cuerpo. Convierta los errores HTTP a su propio tipo de error para propagarlos de forma limpia:

  • → 200 — bytes del PDF sin procesar. Léalos con response.bytes().await? .
  • → 422 — error de compilación de LaTeX. El cuerpo JSON contiene un campo error con un extracto del registro de TeX.
  • → 401 — clave de API no válida o ausente.

\end{rust}

Empiece a compilar LaTeX desde Rust

Obtenga su clave de API y consiga un PDF desde Rust en minutos, sin TeX Live.