SurveyJS Server Integration · PHP / Laravel IV.3 · Export forms to PDF on the server
You work in a private copy of the demo database, kept until it has been unused for 24 hours.

IV.3

Export forms to PDF on the server

You need this: When the PDF must be produced server-side (archiving, email attachments) rather than in the user's browser.

PDF Generator runs on plain Node.js, without a browser. Give it the definition and the answers; get back the filled form. Here the SurveyJS service produces it next to this app, and the route passes the bytes through.

This step on the Server Integration page Source on GitHub Commercial licence: without SURVEYJS_LICENSE_KEY the component shows a licence banner

The SurveyJS service is called at http://surveyjs:3000. If it isn't running, the endpoint answers 503 "SurveyJS service is not running": start it with docker compose up -d surveyjs.

PDF Generator is commercial. The pinned SurveyJS service never calls setLicenseKey(), so its PDFs carry the SurveyJS alert banner even when you set a licence key.

Try this

  1. Press Download PDF next to a claim: GET /api/claims/<id>/pdf answers application/pdf, and the filled form opens in a new tab.
  2. New claims from I.7 appear in this list.
  • Claim #1 · dana.reyes@example.com
  • Claim #2 · li.wei@example.com
  • Claim #3 · sam.osei@example.com

Code that just ran

These regions are read from the files that served this page, the same lines the Server Integration page shows.

Client · shared/client/server-pdf.js · sjs:IV.3.client View on GitHub
// Ask your server for the filled form as a PDF
async function openPdf(id) {
  const tab = window.open("", "_blank");             // open the tab in the click itself: after an await, browsers block pop-ups
  const res = await fetch(`/api/claims/${id}/pdf`);
  if (!res.ok) {
    tab.close();
    throw new Error((await res.json()).error);
  }
  tab.location = URL.createObjectURL(await res.blob());
}
Server · routes/examples/server-pdf.php · sjs:IV.3.server View on GitHub
// GET /api/claims/{id}/pdf — ask the SurveyJS service for the filled form and pass the PDF through
Route::get('/api/claims/{id}/pdf', function (string $id) {
    $data = DB::table('responses')->where('form_id', 'claim')->where('id', $id)->value('data') ?? abort(404, 'Unknown claim');
    $definition = DB::table('forms')->where('key', 'claim')->value('json');
    try {
        $pdf = Http::baseUrl(config('surveyjs.service_url'))->timeout(60)
            ->post('/pdf', ['schema' => json_decode($definition), 'response' => json_decode($data)]);
    } catch (ConnectionException $e) {                                         // cURL error 28 is a timeout
        return str_contains($e->getMessage(), 'cURL error 28')
            ? response()->json(['error' => 'SurveyJS service timed out'], 504)
            : response()->json(['error' => 'SurveyJS service is not running at '.config('surveyjs.service_url')], 503);
    }
    if ($pdf->status() !== 200 || ! str_starts_with($pdf->header('Content-Type'), 'application/pdf')) {   // fail closed
        Log::error('SurveyJS service: unexpected answer from /pdf', ['status' => $pdf->status(), 'body' => substr($pdf->body(), 0, 2000)]);

        return response()->json(['error' => 'SurveyJS service error'], 502);
    }

    return response($pdf->body(), 200, [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => "inline; filename=\"claim-{$id}.pdf\"",
    ]);
});

Definition: shared/definitions/relational-storage.v1.json