\usepackage{aws-lambda}

Génération de PDF LaTeX serverless sur Lambda

Compilez des documents LaTeX en PDF dans des fonctions AWS Lambda avec l'API FormaTeX. Pas de couche TeX Live de 500 Mo, pas d'environnement d'exécution personnalisé : un simple appel HTTP et une réponse PDF.

\section{Pourquoi serverless + LaTeX}

Génération de PDF sans infrastructure

TeX Live pèse plus de 4 Go. L'embarquer dans une couche Lambda est lent, coûteux et fragile. L'API FormaTeX prend entièrement en charge la compilation, vos fonctions restent donc petites et rapides.

Pas de couche TeX Live

Gardez votre package de déploiement Lambda sous 10 Mo. FormaTeX gère l'environnement TeX Live complet sur une infrastructure managée.

Paiement à la compilation

Lambda facture à l'invocation ; FormaTeX facture à la compilation. Réduisez à zéro quand rien ne tourne et montez à des milliers de PDF en cas de charge.

Exécution isolée

Chaque compilation s'exécute dans un conteneur isolé sur les serveurs FormaTeX. Votre fonction Lambda n'exécute jamais de LaTeX arbitraire en local.

\section{Gestionnaire Python}

Gestionnaire Lambda Python

N'utilise que la bibliothèque standard de Python et boto3 (préinstallé sur tous les environnements d'exécution Lambda). Le PDF compilé est stocké dans S3 et la clé de l'objet est renvoyée.

  • Environnement d'exécution : Python 3.12 ou ultérieur
  • Dépendances : boto3 uniquement (inclus dans l'environnement d'exécution Lambda)
  • Délai d'expiration : définissez au moins 30 secondes pour les documents complexes
import json
import os
import urllib.request
import boto3


def lambda_handler(event, context):
    """Compile LaTeX to PDF via FormaTeX API and store the result in S3."""
    source = event.get("source", "")
    engine = event.get("engine", "pdflatex")

    if not source:
        return {
            "statusCode": 400,
            "body": json.dumps({"error": "source is required"}),
        }

    # API key stored as a Lambda environment variable (or fetched from
    # Secrets Manager at cold-start — see the IAM section below).
    api_key = os.environ["FORMATEX_API_KEY"]

    payload = json.dumps({"latex": source, "engine": engine}).encode()
    req = urllib.request.Request(
        "https://api.formatex.io/api/v1/compile",
        data=payload,
        headers={
            "X-API-Key": api_key,
            "Content-Type": "application/json",
        },
        method="POST",
    )

    with urllib.request.urlopen(req, timeout=25) as resp:
        pdf_bytes = resp.read()

    s3 = boto3.client("s3")
    bucket = os.environ["OUTPUT_BUCKET"]
    key = f"pdfs/{context.aws_request_id}.pdf"
    s3.put_object(Bucket=bucket, Key=key, Body=pdf_bytes, ContentType="application/pdf")

    return {
        "statusCode": 200,
        "body": json.dumps({"bucket": bucket, "key": key}),
    }

\section{Gestionnaire Node.js}

Gestionnaire Lambda Node.js

Gestionnaire orienté TypeScript, n'utilisant que les modules natifs de Node.js et le client S3 du SDK AWS v3. La clé API est mise en cache dans la portée du module afin que les invocations à chaud évitent la lecture de la variable d'environnement.

  • Environnement d'exécution : Node.js 20.x ou ultérieur (ESM)
  • Dépendances : @aws-sdk/client-s3 (inclus dans l'environnement d'exécution Lambda)
  • Délai d'expiration : définissez au moins 30 secondes
import https from "node:https"
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"

const s3 = new S3Client({})

// Cache the API key across warm invocations
let cachedApiKey: string | undefined

export const handler = async (event: {
  source: string
  engine?: string
  requestId?: string
}) => {
  const { source, engine = "pdflatex" } = event

  if (!source) {
    return { statusCode: 400, body: JSON.stringify({ error: "source is required" }) }
  }

  if (!cachedApiKey) {
    cachedApiKey = process.env.FORMATEX_API_KEY
  }
  if (!cachedApiKey) throw new Error("FORMATEX_API_KEY is not set")

  const pdfBuffer = await compilePdf(cachedApiKey, source, engine)

  const bucket = process.env.OUTPUT_BUCKET!
  const key = `pdfs/${event.requestId ?? Date.now()}.pdf`

  await s3.send(
    new PutObjectCommand({
      Bucket: bucket,
      Key: key,
      Body: pdfBuffer,
      ContentType: "application/pdf",
    }),
  )

  return { statusCode: 200, body: JSON.stringify({ bucket, key }) }
}

function compilePdf(apiKey: string, source: string, engine: string): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    const body = JSON.stringify({ latex: source, engine })
    const options = {
      hostname: "api.formatex.io",
      path: "/api/v1/compile",
      method: "POST",
      headers: {
        "X-API-Key": apiKey,
        "Content-Type": "application/json",
        "Content-Length": Buffer.byteLength(body),
      },
    }
    const req = https.request(options, (res) => {
      const chunks: Buffer[] = []
      res.on("data", (c: Buffer) => chunks.push(c))
      res.on("end", () => {
        if (res.statusCode !== 200) {
          reject(new Error(`FormaTeX error: HTTP ${res.statusCode}`))
        } else {
          resolve(Buffer.concat(chunks))
        }
      })
    })
    req.on("error", reject)
    req.write(body)
    req.end()
  })
}

\section{Autorisations IAM}

Stocker la clé API dans Secrets Manager

Pour les charges de travail en production, stockez votre clé API FormaTeX dans AWS Secrets Manager plutôt que dans une simple variable d'environnement. Attachez la politique ci-dessous au rôle d'exécution de votre Lambda et récupérez le secret au démarrage à froid.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadFormaTeXApiKey",
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:formatex/api-key-*"
    },
    {
      "Sid": "WriteOutputBucket",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::your-output-bucket/pdfs/*"
    }
  ]
}

# Store the secret in Secrets Manager (one-time setup):
aws secretsmanager create-secret \
  --name formatex/api-key \
  --secret-string '{"FORMATEX_API_KEY":"your-key-here"}'

# Then fetch it at cold-start in Python:
# import boto3, json
# secret = boto3.client("secretsmanager").get_secret_value(SecretId="formatex/api-key")
# api_key = json.loads(secret["SecretString"])["FORMATEX_API_KEY"]

\section{Démarrages à froid}

Réduire la latence des démarrages à froid

Mettre les secrets en cache dans la portée du module

Récupérez votre clé API depuis Secrets Manager une seule fois, en dehors de la fonction du gestionnaire. La valeur est réutilisée pour toutes les invocations à chaud sans appel réseau supplémentaire.

Utiliser la concurrence provisionnée pour les chemins sensibles à la latence

Activez la concurrence provisionnée sur la version Lambda pour éliminer complètement les démarrages à froid sur les points de terminaison de génération de PDF exposés aux utilisateurs. Utilisez la concurrence à la demande pour les tâches par lots en arrière-plan.

Définir un délai d'expiration réaliste

Les documents LaTeX complexes avec TikZ, bibliographie ou plusieurs passes de compilation peuvent prendre 10 à 20 secondes. Définissez le délai d'expiration de la Lambda à au moins 30 secondes et configurez celui de votre intégration API Gateway ou ALB en conséquence.

\section{Guides associés}

Autres guides SDK et langages

L'API FormaTeX est un simple point de terminaison HTTP. Utilisez-la depuis n'importe quel langage ou environnement d'exécution.

Prêt à générer des PDF sur Lambda ?

Offre gratuite : 15 compilations API par mois. Aucune carte bancaire requise.