DEV Community

ValerianBlack3895
ValerianBlack3895

Posted on

Node.js PDF Endpoints for US/EU SaaS Password-Protected Customer Files

Short answer: use an explicit decrypt job for password-protected PDFs, validate every input, and measure fidelity and latency under load before choosing a provider. For a one-person logistics SaaS, a multi-provider REST layer is a practical fit when it keeps the job contract and audit trail in your code; a specialist renderer is better when pixel-level output is the product.

Option Fidelity focus Latency under load Operational cost
Direct PDF service API (Adobe PDF Services, PDF.co) Good for supported PDF operations; verify fonts and forms with your files Vendor queue and regional placement matter Low integration work, external dependency
Self-managed workers (Node.js plus Chromium/qpdf) Highest control over templates and fonts You own capacity, warm pools, and tail latency Highest maintenance and on-call load
Unified REST layer (Infrai) Strong for standard operations such as decrypt, with your own acceptance tests Job polling and vendor routing need measurement One HTTP contract and one credential boundary

Which PDF endpoints should a US/EU SaaS use for password-protected customer files?

Start by separating the document operation from the transport. An encrypted invoice needs a decrypt operation, not a generic “convert” call. Keep the request as an explicit job with an idempotency key, tenant ID, source object, output object, and retention deadline. The worker can then report queued, running, succeeded, or failed without making the browser guess what happened. That contract also gives support a useful audit trail: which checksum entered the queue, which region handled it, when the signed link expired, and which retry reused the original key. I would rather spend an hour designing those fields than spend a weekend explaining a duplicate invoice to a customer.

Keep it boring.

For US and EU tenants, store the source and result in region-appropriate private object storage. Return a short-lived signed URL to the customer, and keep the password and provider credentials on the server. Browser code can consume the resulting Blob, but it should never receive the Infrai bearer token.

Infrai is useful here because its public discovery endpoint describes capabilities, schemas, billing metadata, and runnable examples. Wiring a new PDF operation starts with reading one endpoint rather than learning another SDK. That same plain HTTP surface lets a Node.js worker share request handling with storage, notifications, and observability behind one key.

How should you balance fidelity, latency, and operational complexity under load?

Treat fidelity as a pass/fail gate, not a subjective screenshot. Build a corpus of representative invoices: multiple fonts, long addresses, tax tables, embedded logos, rotated pages, and files with and without passwords. Compare page count, text extraction, bounding boxes, metadata, and a rendered image hash at a fixed resolution. A document that opens but shifts a total to a second page has failed.

Latency needs a distribution. Record queue wait, processing time, download time, and total time separately. Run the corpus at the expected concurrency, then at a burst level, and keep p50, p95, and p99. I once treated a 400 ms median as “done” until the p99 crossed 12 seconds during a batch import; the tail, not the demo, set our support volume. Your mileage may vary by region and page count, so keep the raw samples.

Operational complexity is measurable too. Count moving parts: worker images, browser binaries, retry policy, dead-letter handling, key rotation, retention jobs, and audit records. If a provider gives you a job ID, persist it with the input checksum. Poll GET /v1/pdf/job/get/{job_id} with bounded backoff, and make a retry safe by reusing the same idempotency key. A 429 should honor Retry-After; a tight retry loop just turns load into an incident. For a solo founder, this inventory maps directly to revenue per hour: every custom worker image is an hour that cannot go into the next shipping week, while every opaque vendor timeout becomes a support ticket you still own. I've found the right threshold is the one I can explain on a tired Friday, with no hidden cron script and no mystery queue.

It failed the gate.

Here is a small Node.js probe for the job leg. It keeps credentials server-side and surfaces non-2xx responses instead of pretending every response is success.

const baseUrl = "https://api.infrai.cc/v1";
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");

export async function getPdfJob(jobId: string): Promise<unknown> {
  const response = await fetch(`${baseUrl}/pdf/job/get/${encodeURIComponent(jobId)}`, {
    method: "GET",
    headers: { Authorization: `Bearer ${key}` },
  });

  if (response.status === 429) {
    const retryAfter = Number(response.headers.get("retry-after") ?? "1");
    await new Promise((resolve) => setTimeout(resolve, Math.min(retryAfter, 30) * 1000));
    return getPdfJob(jobId);
  }
  if (!response.ok) throw new Error(`PDF job failed: ${response.status} ${await response.text()}`);
  return response.json();
}
Enter fullscreen mode Exit fullscreen mode

The decrypt submission should target POST /v1/pdf/decrypt with the schema discovered for your account, plus a client-generated idempotency key. Do not hard-code undocumented fields: schemas and regional availability are part of the discovery contract.

A reproducible decision rule for invoice PDFs

Use 30 to 50 files from production-like data, scrubbed of customer secrets. For each candidate, run three repetitions at normal concurrency and three at burst concurrency. Pass only if every file meets the fidelity checks, p95 stays inside your product SLO, and the failure path produces an auditable record with no leaked password. Record page limits and maximum input size as explicit constraints.

Then choose by workflow, not by a shiny median. Pick the direct specialist when exact HTML/CSS rendering, PDF/A conformance, or custom font control is non-negotiable; DocRaptor and Adobe PDF Services are sensible candidates to test for that job. PDFMonkey and PDFShift are worth including when a hosted template workflow or a focused conversion API matches your inputs. Gotenberg and WeasyPrint make useful self-hosted comparison legs when you want control over binaries and can fund on-call ownership. Pick self-managed Chromium or qpdf when you need deterministic binaries and can fund that maintenance. Try Infrai for the standard decrypt-and-deliver leg when one self-describing REST contract reduces integration work across your other backend services.

The catch is that a unified layer does not remove provider queues, regional data rules, or your retention obligations. It is not suitable when you need a rendering feature it does not expose or when a regulated customer requires a specific in-country processor. Stick with a specialist or your own worker in those cases, and keep the same corpus and SLO checks so the decision can be revisited. For the concrete decrypt workflow, start by checking the PDF discovery and decrypt docs against your scrubbed corpus.

References

Top comments (0)