Bill to: {{ customer_name }}
Amount due: {{ amount }}
""", variables_schema: %{ "required" => ["number", "customer_name", "amount"] } ) ``` ### Render to PDF ```elixir {:ok, pdf_binary} = SferaDoc.render("invoice", %{ "number" => "INV-0042", "customer_name" => "Acme Corp", "amount" => "$1,200.00" }) File.write!("invoice.pdf", pdf_binary) ``` ### Missing variables If required variables are absent, rendering is short-circuited before any parsing or Chrome calls: ```elixir {:error, {:missing_variables, ["amount"]}} = SferaDoc.render("invoice", %{"number" => "1", "customer_name" => "Acme"}) ``` ### Template versioning Every `update_template/3` call creates a new version and activates it. Previous versions are preserved. ```elixir {:ok, v1} = SferaDoc.create_template("report", "Draft
") {:ok, v2} = SferaDoc.update_template("report", "Final
") # List all versions {:ok, versions} = SferaDoc.list_versions("report") # => [%Template{version: 2, is_active: true}, %Template{version: 1, is_active: false}] # Render a specific version {:ok, pdf} = SferaDoc.render("report", %{}, version: 1) # Roll back to a previous version {:ok, _} = SferaDoc.activate_version("report", 1) ``` ### Other operations ```elixir # Fetch template metadata (no rendering) {:ok, template} = SferaDoc.get_template("invoice") {:ok, template} = SferaDoc.get_template("invoice", version: 2) # List all templates (active version per name) {:ok, templates} = SferaDoc.list_templates() # Delete all versions of a template :ok = SferaDoc.delete_template("invoice") ``` ## Configuration Reference ```elixir # Storage backend (required) config :sfera_doc, :store, adapter: SferaDoc.Store.Ecto, repo: MyApp.Repo, table_name: "sfera_doc_templates" # optional, compile-time # Redis connection (when using Redis adapter) config :sfera_doc, :redis, host: "localhost", port: 6379 # Or with a URI: config :sfera_doc, :redis, "redis://localhost:6379" # Parsed template AST cache (default: enabled, 300s TTL) config :sfera_doc, :cache, enabled: true, ttl: 300 # ChromicPDF options (passed through to ChromicPDF supervisor) config :sfera_doc, :chromic_pdf, session_pool: [size: 2, timeout: 10_000] # Template engine adapter (defaults to Solid) config :sfera_doc, :template_engine, adapter: SferaDoc.TemplateEngine.Solid # PDF engine adapter (defaults to ChromicPDF) config :sfera_doc, :pdf_engine, adapter: SferaDoc.PdfEngine.ChromicPDF ``` ### PDF hot cache (opt-in) A fast, ephemeral front-tier cache. Repeat requests with identical variables are served from Redis or ETS without touching the object store or Chrome. ```elixir # Redis (distributed, recommended for multi-node) config :sfera_doc, :pdf_hot_cache, adapter: :redis, ttl: 60 # seconds # ETS (single-node, zero external deps) config :sfera_doc, :pdf_hot_cache, adapter: :ets, ttl: 300 ``` > **Warning:** PDFs can be 100 KB – 10 MB or more. Keep TTLs short and monitor memory. > For Redis, set `maxmemory-policy allkeys-lru`. ### PDF object store (opt-in) A durable, persistent tier, the **source of truth** for rendered PDFs. PDFs survive BEAM restarts. On a cache hit the PDF is returned directly and the hot cache is populated, avoiding Chrome entirely. ```elixir # Amazon S3 (requires :ex_aws and :ex_aws_s3) config :sfera_doc, :pdf_object_store, adapter: SferaDoc.Pdf.ObjectStore.S3, bucket: "my-pdfs", prefix: "sfera_doc/" # optional # Azure Blob Storage (requires :azurex) config :sfera_doc, :pdf_object_store, adapter: SferaDoc.Pdf.ObjectStore.Azure, container: "my-pdfs" # azurex credentials (can also be passed inline in the config above) config :azurex, Azurex.Blob.Config, storage_account_name: "mystorageaccount", storage_account_key: "base64encodedkey==" # Local / shared file system (no extra deps) config :sfera_doc, :pdf_object_store, adapter: SferaDoc.Pdf.ObjectStore.FileSystem, path: "/var/data/pdfs" ``` Both tiers are fully independent. You can use the object store without a hot cache, the hot cache without the object store, both together, or neither (generate on every request, the original behaviour). ## Storage Backends | Adapter | Use case | |---|---| | `SferaDoc.Store.Ecto` | Production: PostgreSQL, MySQL, SQLite | | `SferaDoc.Store.ETS` | Development and testing only (data lost on restart) | | `SferaDoc.Store.Redis` | Distributed / Redis-heavy stacks | ## Telemetry SferaDoc emits the following telemetry events: | Event | Measurements | Metadata | |---|---|---| | `[:sfera_doc, :render, :start]` | `system_time` | `template_name` | | `[:sfera_doc, :render, :stop]` | `duration` | `template_name` | | `[:sfera_doc, :render, :exception]` | `duration` | `template_name`, `error` |