<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TommyDev</title>
    <description>The latest articles on DEV Community by TommyDev (@tommaso_dicecco).</description>
    <link>https://dev.to/tommaso_dicecco</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4035068%2F29752984-708d-4d25-bb2e-7815e448d7b5.png</url>
      <title>DEV Community: TommyDev</title>
      <link>https://dev.to/tommaso_dicecco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tommaso_dicecco"/>
    <language>en</language>
    <item>
      <title>How to Generate PDF Invoices in PHP (Yii2 / Laravel) via REST API</title>
      <dc:creator>TommyDev</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:43:38 +0000</pubDate>
      <link>https://dev.to/tommaso_dicecco/how-to-generate-pdf-invoices-in-php-yii2-laravel-via-rest-api-5hmm</link>
      <guid>https://dev.to/tommaso_dicecco/how-to-generate-pdf-invoices-in-php-yii2-laravel-via-rest-api-5hmm</guid>
      <description>&lt;p&gt;Generating PDF invoices in PHP using traditional libraries like mPDF or Dompdf often leads to heavy server memory consumption, broken CSS layouts, and maintenance headaches. &lt;/p&gt;

&lt;p&gt;By offloading the rendering engine to &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;QuartzAPI&lt;/a&gt;, your PHP application only needs to perform a fast, lightweight HTTP POST request with a JSON payload containing the invoice data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PHP 7.4+ or 8.x&lt;/li&gt;
&lt;li&gt;A QuartzAPI account and API Key&lt;/li&gt;
&lt;li&gt;An active Template ID created in the QuartzAPI Visual Builder (e.g., tpl_invoice_v1)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Prepare Your JSON Payload Data
&lt;/h2&gt;

&lt;p&gt;Whether you are fetching invoice data from Eloquent (Laravel) or Active Record (Yii2), structure your data as a clean associative array matching your template placeholders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$invoiceData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'template_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'tpl_invoice_v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'invoice_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'INV-2026-0042'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y-m-d'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'customer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Acme Corporation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'vat'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'US123456789'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'billing@acme.com'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'items'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Web Development Services'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;85.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;850.00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Cloud Hosting (Annual)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;240.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;240.00&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'subtotal'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1090.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'tax'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;239.80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'grand_total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1329.80&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Implementation in Laravel
&lt;/h2&gt;

&lt;p&gt;In Laravel, use the built-in HTTP Client (Illuminate\Support\Facades\Http) to send the payload and handle the binary response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;downloadPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$invoiceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'template_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'tpl_invoice_v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'invoice_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'INV-2026-0042'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'grand_total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1329.80&lt;/span&gt;
            &lt;span class="p"&gt;]];&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'services.quartzapi.key'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.quartzapi.com/v1/render'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Failed to generate PDF invoice.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/pdf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'Content-Disposition'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'attachment; filename="Invoice-2026-0042.pdf"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Implementation in Yii2
&lt;/h2&gt;

&lt;p&gt;In Yii2, you can use the yii\httpclient\Client component or standard cURL inside a Controller action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;app\controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yii&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;yii\web\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;yii\httpclient\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;actionDownloadPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'template_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'tpl_invoice_v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'invoice_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'INV-2026-0042'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'grand_total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1329.80&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.quartzapi.com/v1/render'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nc"&gt;Yii&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'quartzApiKey'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isOk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\yii\web\ServerErrorHttpException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PDF rendering service unavailable.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Yii&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sendContentAsFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'Invoice-2026-0042.pdf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'mimeType'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/pdf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'inline'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Benefits over mPDF / Dompdf
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero RAM overhead:&lt;/strong&gt; Server memory stays constant regardless of document size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No CSS workarounds:&lt;/strong&gt; Layout is maintained in the visual drag-and-drop editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant updates:&lt;/strong&gt; Change design elements without pushing code changes or running deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How do you currently manage PDF layout updates in your PHP projects? Let's discuss in the comments below!&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why PDF Generation in HTML is Painful (And How to Fix It with Visual Templates &amp; REST APIs)</title>
      <dc:creator>TommyDev</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:01:23 +0000</pubDate>
      <link>https://dev.to/tommaso_dicecco/why-pdf-generation-in-html-is-painful-and-how-to-fix-it-with-visual-templates-rest-apis-5c99</link>
      <guid>https://dev.to/tommaso_dicecco/why-pdf-generation-in-html-is-painful-and-how-to-fix-it-with-visual-templates-rest-apis-5c99</guid>
      <description>&lt;p&gt;If you’ve ever had to build a dynamic PDF report in PHP, Node, or Python, you probably know the exact moment the nightmare begins.&lt;/p&gt;

&lt;p&gt;It usually starts like this: &lt;em&gt;"We just need a simple invoice/report PDF."&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;So you install a library like mPDF, wkhtmltopdf, or Dompdf. You write clean HTML and modern CSS, hit render... and everything breaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexbox and Grid aren't supported.&lt;/li&gt;
&lt;li&gt;Page breaks split tables mid-row.&lt;/li&gt;
&lt;li&gt;Custom fonts fail to render.&lt;/li&gt;
&lt;li&gt;Memory usage spikes every time two users request a document simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After spending 8 hours tweaking inline CSS and table borders, you end up with code that feels fragile and hard to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with "HTML-to-PDF" Libraries
&lt;/h2&gt;

&lt;p&gt;Most legacy HTML-to-PDF converters rely on outdated browser rendering engines (often stripped-down forks from 10+ years ago). &lt;/p&gt;

&lt;p&gt;When you build complex document structures — like multi-page financial statements, dynamic invoices with varying line items, or clean visual charts — you run into three main roadblocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Broken Layouts:&lt;/strong&gt; Modern CSS specs simply don't translate correctly to print/PDF view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Overhead:&lt;/strong&gt; Rendering PDFs on your application server steals RAM and CPU cycles away from your core API logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endless Code Revisions:&lt;/strong&gt; Every time marketing or accounting wants to change a logo, color, or table header, a developer has to modify code, re-test, and deploy.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  A Better Approach: Separate Design from Data
&lt;/h2&gt;

&lt;p&gt;What if your backend didn't have to care about fonts, CSS, or page boundaries at all? &lt;/p&gt;

&lt;p&gt;What if your code only did what it does best — &lt;strong&gt;passing JSON data&lt;/strong&gt; — while the layout lived safely in a visual builder designed specifically for print media?&lt;/p&gt;

&lt;p&gt;That’s the exact workflow we designed with &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;QuartzAPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the architecture looks:&lt;/p&gt;

&lt;p&gt;[ Your Backend / DB ] ---&amp;gt; ( Sends JSON Payload ) ---&amp;gt; [ QuartzAPI ] ---&amp;gt; ( Returns PDF / Image )&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Design the Template Visually
&lt;/h2&gt;

&lt;p&gt;Instead of fighting CSS in code, you build your document template in a drag-and-drop editor. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag in dynamic text, tables, QR codes, images, and headers.&lt;/li&gt;
&lt;li&gt;Bind elements to variable names (e.g., {{invoice.number}}, {{customer.name}}).&lt;/li&gt;
&lt;li&gt;Set page break rules and repeating table headers with a single click.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the editor is built strictly for document layouts, what you see in the builder is exactly what gets rendered in the PDF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nv9sykhrjen92lw2utc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nv9sykhrjen92lw2utc.png" alt=" " width="799" height="367"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Render via a Simple REST API Call
&lt;/h2&gt;

&lt;p&gt;Once your template is saved, generating a PDF from your application is just a single HTTP POST request.&lt;/p&gt;

&lt;p&gt;Here is an example using cURL:&lt;/p&gt;

&lt;p&gt;curl -X POST "&lt;a href="https://api.quartzapi.com/v1/render" rel="noopener noreferrer"&gt;https://api.quartzapi.com/v1/render&lt;/a&gt;" \&lt;br&gt;
  -H "Authorization: Bearer YOUR_API_KEY" \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{&lt;br&gt;
    "template_id": "tpl_invoice_2026",&lt;br&gt;
    "data": {&lt;br&gt;
      "invoice_number": "INV-2026-001",&lt;br&gt;
      "client_name": "Acme Corp",&lt;br&gt;
      "items": [&lt;br&gt;
        { "description": "API Integration", "price": 450.00 },&lt;br&gt;
        { "description": "Cloud Hosting Setup", "price": 150.00 }&lt;br&gt;
      ],&lt;br&gt;
      "total": 600.00&lt;br&gt;
    }&lt;br&gt;
  }'&lt;/p&gt;

&lt;p&gt;Or in JavaScript (Node.js / Fetch):&lt;/p&gt;

&lt;p&gt;const response = await fetch('&lt;a href="https://api.quartzapi.com/v1/render" rel="noopener noreferrer"&gt;https://api.quartzapi.com/v1/render&lt;/a&gt;', {&lt;br&gt;
  method: 'POST',&lt;br&gt;
  headers: {&lt;br&gt;
    'Authorization': 'Bearer YOUR_API_KEY',&lt;br&gt;
    'Content-Type': 'application/json'&lt;br&gt;
  },&lt;br&gt;
  body: JSON.stringify({&lt;br&gt;
    template_id: 'tpl_invoice_2026',&lt;br&gt;
    data: {&lt;br&gt;
      invoice_number: 'INV-2026-001',&lt;br&gt;
      client_name: 'Acme Corp',&lt;br&gt;
      items: [&lt;br&gt;
        { description: 'API Integration', price: 450.00 }&lt;br&gt;
      ]&lt;br&gt;
    }&lt;br&gt;
  })&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;const pdfBuffer = await response.arrayBuffer();&lt;/p&gt;

&lt;p&gt;Your server stays lightweight, you save server resources, and you never have to debug mPDF line height bugs again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Benefits of Using a Dedicated Document API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Layout Code in Backend:&lt;/strong&gt; Your controllers remain clean and focused on business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Design Updates:&lt;/strong&gt; Non-developers (or developers wanting to save time) can update the document layout in the visual builder without requiring a new software release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Output:&lt;/strong&gt; Built-in handling for page numbers, dynamic table pagination, and high-resolution rendering.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How do you handle PDF generation?
&lt;/h2&gt;

&lt;p&gt;I’m curious to know how other developers in the community tackle this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are you still using libraries like mPDF, PDFKit, or Puppeteer?&lt;/li&gt;
&lt;li&gt;Have you moved to headless Chrome or external APIs?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s discuss in the comments below! If you'd like to test the visual builder workflow, feel free to check out &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;QuartzAPI&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystalreport</category>
      <category>api</category>
      <category>json</category>
      <category>php</category>
    </item>
    <item>
      <title>Stop Coding PDF Layouts: How to Build Clean Templates Visually</title>
      <dc:creator>TommyDev</dc:creator>
      <pubDate>Thu, 23 Jul 2026 15:21:41 +0000</pubDate>
      <link>https://dev.to/tommaso_dicecco/stop-coding-pdf-layouts-how-to-build-clean-api-templates-visually-1806</link>
      <guid>https://dev.to/tommaso_dicecco/stop-coding-pdf-layouts-how-to-build-clean-api-templates-visually-1806</guid>
      <description>&lt;p&gt;If you've ever had to build dynamic PDF documents—invoices, shipping labels, compliance reports, or receipts—you know the pain. &lt;/p&gt;

&lt;p&gt;Usually, developers end up down one of two painful paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pixel-pushing in code:&lt;/strong&gt; Using low-level PDF libraries (like PDFKit, FPDF, or TCPDF) where adding a simple border or moving an image 10px down means tweaking coordinate arrays and re-running backend code 50 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML-to-PDF nightmares:&lt;/strong&gt; Using headless browsers (Puppeteer, WKHTMLTOPDF) that consume gigabytes of RAM on your server and break pagination the moment a table spans across two pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a much cleaner architectural approach: &lt;strong&gt;separating the document design from your application logic.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Concept: Visual Design + JSON Payload
&lt;/h2&gt;

&lt;p&gt;When building web applications, you wouldn't mix your CSS directly into your database queries. So why mix PDF visual design into your backend controllers?&lt;/p&gt;

&lt;p&gt;By decoupling the template design from the rendering execution, your workflow transforms into two simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Design visually:&lt;/strong&gt; Create and tweak headers, dynamic tables, page numbers, logos, and conditional visibility in a visual drag-and-drop editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger via REST API:&lt;/strong&gt; Send a clean JSON payload with your dynamic data to an API endpoint that handles the heavy rendering lifting.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How the QuartzAPI Visual Builder Solves This
&lt;/h2&gt;

&lt;p&gt;This design-first methodology is the core engine behind &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;QuartzAPI&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Instead of writing lines of boilerplate code to handle page breaks, repeating table headers, or dynamic text wrapping, the &lt;strong&gt;QuartzAPI Visual Builder&lt;/strong&gt; lets you construct document templates visually directly in the browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drag-and-Drop Components:&lt;/strong&gt; Place text boxes, images, dynamic data fields, and barcodes exactly where you need them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Tables:&lt;/strong&gt; Define repeating rows for line items that automatically handle multi-page overflow and header repetition cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Binding:&lt;/strong&gt; Bind visual fields directly to your JSON schema variables using simple syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Preview:&lt;/strong&gt; Test how your template renders with real sample JSON payloads before sending a single HTTP request from your app.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the Code Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;Once your template is saved in the builder, generating a complex, multi-page PDF from your backend (Node.js, PHP, Python, C#, etc.) becomes as simple as sending a standard POST request.&lt;/p&gt;

&lt;p&gt;Here is an example payload:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "template_id": "tpl_invoice_v2",&lt;br&gt;
  "data": {&lt;br&gt;
    "invoice_number": "INV-2026-089",&lt;br&gt;
    "customer": {&lt;br&gt;
      "name": "Acme Corp",&lt;br&gt;
      "vat": "US123456789"&lt;br&gt;
    },&lt;br&gt;
    "items": [&lt;br&gt;
      { "description": "Cloud API Subscription", "qty": 1, "price": 49.00 },&lt;br&gt;
      { "description": "Custom Integration Support", "qty": 2, "price": 150.00 }&lt;br&gt;
    ],&lt;br&gt;
    "total": 349.00&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Your backend application doesn't care about page dimensions, fonts, or margin calculations. It just supplies the raw data—&lt;strong&gt;QuartzAPI handles the rest and returns the generated PDF.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Decoupling Matters for Teams
&lt;/h2&gt;

&lt;p&gt;Shifting PDF generation to a visual builder + REST API architecture brings massive benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Server Overhead:&lt;/strong&gt; Your application server doesn't freeze or run out of memory spawning headless browser processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Design Iterations:&lt;/strong&gt; Need to update the company logo, change a color scheme, or adjust footer legal text? Update the template in the visual builder &lt;strong&gt;without redeploying your backend code&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Codebases:&lt;/strong&gt; Keep your backend clean, focused purely on business logic and data retrieval.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;If your team is still spending expensive engineering hours wrestling with PDF layouts or maintaining bloated legacy reporting servers, it’s time to modernize the stack.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;QuartzAPI&lt;/a&gt; to try out the visual builder and see how effortless document generation can be.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What tools are you currently using for PDF generation in production? Let's discuss in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>pdf</category>
      <category>crystal</category>
      <category>report</category>
    </item>
    <item>
      <title>How to Replace Legacy Reporting Tools with Light REST APIs (And Save Infrastructure Costs)</title>
      <dc:creator>TommyDev</dc:creator>
      <pubDate>Sat, 18 Jul 2026 10:13:07 +0000</pubDate>
      <link>https://dev.to/tommaso_dicecco/how-to-replace-legacy-reporting-tools-with-light-rest-apis-and-save-infrastructure-costs-4gpp</link>
      <guid>https://dev.to/tommaso_dicecco/how-to-replace-legacy-reporting-tools-with-light-rest-apis-and-save-infrastructure-costs-4gpp</guid>
      <description>&lt;p&gt;If you have ever worked on enterprise applications, ERPs, or legacy software, you probably know &lt;strong&gt;Crystal Reports&lt;/strong&gt;. For decades, it was the gold standard for creating invoices, stock sheets, and financial reports. You design a &lt;code&gt;.rpt&lt;/code&gt; file on a desktop app, link it via ODBC to your SQL Server or SAP HANA database, and let the runtime generate the PDF.&lt;/p&gt;

&lt;p&gt;But as the world moved to &lt;strong&gt;modern web architectures, Docker containers, microservices, and multi-tenant SaaS platforms&lt;/strong&gt;, this approach started to show its age. &lt;/p&gt;

&lt;p&gt;Keeping a dedicated Windows Server VM alive just to run a legacy reporting engine—while the rest of your stack runs smoothly on Linux containers—is a major architectural headache and an unnecessary infrastructure cost.&lt;/p&gt;

&lt;p&gt;Let’s break down why legacy reporting tools break in the cloud era and how you can replace them with a lightweight &lt;strong&gt;JSON-to-PDF REST API&lt;/strong&gt; architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3 Major Bottlenecks of Desktop-Driven Reporting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Windows Server Lock-in
&lt;/h3&gt;

&lt;p&gt;Crystal Reports runtime environments are tightly coupled with Windows. If your modern application is containerized with Docker and deployed on AWS ECS, Azure Kubernetes, or Linux-based cloud VMs, you are forced to spin up and maintain a separate, expensive Windows EC2 instance just for rendering PDFs. This fragments your CI/CD pipelines and jacks up your monthly cloud bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Direct Database Coupling
&lt;/h3&gt;

&lt;p&gt;Legacy reports pull data by opening their own connections (ODBC/OLEDB) directly to the database to run internal queries or stored procedures. In a modern API-first or multi-tenant system, this is an architectural anti-pattern. It creates security vulnerabilities, bypasses your backend application logic, and leads to database connection exhaustion under heavy reporting loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Painful Updates and Deployment Cycles
&lt;/h3&gt;

&lt;p&gt;When a customer asks to move a logo or change a tax label on an invoice, a developer has to open the desktop designer, fight with complex Crystal Syntax formulas, save the file, and manually deploy it to the server. There is no concept of version control, automated testing, or continuous delivery for these layouts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Modern Alternative: Decoupled Template Engines + Web APIs
&lt;/h2&gt;

&lt;p&gt;The clean way to handle document generation in modern SaaS is to completely decouple the &lt;strong&gt;data layer&lt;/strong&gt; from the &lt;strong&gt;presentation layer&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Your backend application (whether written in Node.js, PHP, Python, or .NET) handles the query, aggregates the data, and structures it into a clean, readable JSON payload. It then sends this payload via a quick HTTP POST request to a specialized cloud microservice that handles the heavy rendering work.&lt;/p&gt;

&lt;p&gt;Here is how the workflow changes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Legacy Desktop Reporting&lt;/th&gt;
&lt;th&gt;Modern Cloud Document API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dedicated Windows Server, license costs&lt;/td&gt;
&lt;td&gt;Serverless / Managed Cloud API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DB Drivers (ODBC), native runtime dependencies&lt;/td&gt;
&lt;td&gt;Simple HTTP REST call (Universal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout Design&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proprietary Desktop Designer&lt;/td&gt;
&lt;td&gt;Cloud-based Web Editor (HTML/CSS-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Flow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Report pulls data directly from DB&lt;/td&gt;
&lt;td&gt;Application pushes verified JSON data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  From Stored Procedures to Clean JSON: A Practical Example
&lt;/h2&gt;

&lt;p&gt;Instead of maintaining a visual file on a server, you design the template once using a cloud-based editor with standard placeholders (like &lt;code&gt;{{customer.name}}&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;When a user clicks "Print Invoice" or an automated job triggers a monthly report, your application database code doesn't need to know anything about margins, page breaks, or font layouts. It just builds a JSON dictionary and sends it out.&lt;/p&gt;

&lt;p&gt;Here is how easy it is to generate a document using a simple cURL request:&lt;/p&gt;

&lt;p&gt;curl -X POST &lt;a href="https://api.quartzapi.com/v1/generate-document" rel="noopener noreferrer"&gt;https://api.quartzapi.com/v1/generate-document&lt;/a&gt; \&lt;br&gt;
  -H "Authorization: Bearer YOUR_SECRET_API_KEY" \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{&lt;br&gt;
    "template_id": "warehouse_stock_report",&lt;br&gt;
    "folder_id": "monthly_reports_2026",&lt;br&gt;
    "data": {&lt;br&gt;
      "warehouse": "Eastern Logistics Hub",&lt;br&gt;
      "manager": "Alex Miller",&lt;br&gt;
      "date": "2026-07-18",&lt;br&gt;
      "items": [&lt;br&gt;
        { "sku": "SKU-001", "name": "Electronic Component A", "stock": 1500 },&lt;br&gt;
        { "sku": "SKU-002", "name": "Shielded Cable 5m", "stock": 420 }&lt;br&gt;
      ]&lt;br&gt;
    }&lt;br&gt;
  }'&lt;/p&gt;

&lt;p&gt;The cloud engine processes the payload, builds the multi-page PDF (automatically handling table overflow and headers across pages), archives it securely in a cloud folder, and returns a direct download URL. Your main production server experiences &lt;strong&gt;zero CPU load&lt;/strong&gt; during the intensive PDF rendering process.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architectural Benefits for Software Houses
&lt;/h2&gt;

&lt;p&gt;Switching to a stateless, API-driven documentation workflow yields immediate benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Infrastructure Overhead:&lt;/strong&gt; You can instantly decommission your Windows Server VMs. Your cloud stack remains clean, containerized, and strictly Linux-based.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safer Multi-Tenancy:&lt;/strong&gt; You can pass tenant-specific configuration, branding assets, and isolated data dynamically inside the JSON payload using the exact same base template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Time-to-Market:&lt;/strong&gt; Non-technical team members or support agents can tweak document layouts, update typos, or swap logos inside a web-based dashboard without bothering the backend developers or triggering a software redeploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Tools like Crystal Reports did their job wonderfully in the desktop and client-server era. However, modern SaaS environments demand lightweight, decoupled, and stateless tools. Moving your reporting to a REST API structure eliminates infrastructure debt and allows your core application to focus entirely on what it does best: handling business logic.&lt;/p&gt;

&lt;p&gt;How are you handling high-volume PDF generation in your current cloud stacks? Let's discuss in the comments below!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Note: If you are looking for a simple way to offload document generation from your servers, I am currently building **QuartzAPI&lt;/em&gt;&lt;em&gt;—a cloud-native JSON-to-PDF platform designed specifically to replace heavy legacy reporting systems. We just launched our free public beta. Check out the project and our deeper architectural guides at &lt;a href="https://quartzapi.com" rel="noopener noreferrer"&gt;quartzapi.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>cloud</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
