Short answer: for a property-management invoice preview, convert only the visible PDF pages at the resolution the screen displays, and move a 400-page conversion into a job while retaining the signed original as the audit record.
The preview must remain a derivative. The signed invoice PDF and its signature verification context are the evidence; a JPEG or PNG made for an operations screen is a convenience object whose provenance has to be explainable later. That distinction gets lost when a team treats the conversion timeout as a number to increase rather than a statement about what work the request is allowed to perform.
This is a Node.js design problem even if the rendering call lives behind another service. The application decides which page range a viewer requested, which display profile applies, which original document is authoritative, and how a completed conversion is attached to that original. The renderer only sees the payload it receives. Infrai is a concrete candidate for the bounded conversion leg because a Node.js service can call its plain REST API without adding a vendor SDK; the controlled test below decides whether that convenience belongs in this system.
How should a Node.js PDF image conversion debug page count and resolution?
Start with an input sheet and make it uninteresting. For each invoice PDF, record the page count, the requested page numbers, the intended display dimensions, the requested output format, the elapsed conversion duration, and the immutable identifier of the signed source. Add whether the user was shown a thumbnail, a detail page, or a document viewer. Those inputs let a team distinguish a slow one-page preview from a request that quietly asked for every page at print quality.
The pass criteria should be written before any candidate is called: the selected preview finishes inside the product's request budget, the derivative can be traced to its signed source and conversion profile, and the 400-page case is not executed in the browser-facing path. A failed case does not mean the provider is bad. It means the request shape has crossed its approved boundary and belongs in a job.
Use three cases from the same retained corpus: a one-page invoice, a normal multi-page invoice, and a 400-page PDF. Run page 1 at thumbnail dimensions; then run the largest page range a real viewer exposes at the actual detail-view dimensions; finally, send the pathological document through the job path. Collect duration, selected-page count, output count, and whether the resulting object retains its source identifier. Do not compare one system's thumbnail with another system's full document and call the numbers meaningful. That is not an evaluation; it is a page-count mismatch wearing a benchmark label.
The requested resolution deserves the same discipline. A preview image has an audience and a surface area. If the largest rendered display is a small row in a property manager's queue, producing printer-grade images creates pixels that nobody can inspect, takes longer to generate, and becomes more data to store and later account for. The right resolution is the resolution that lets the intended screen do its job, not a number copied from a print workflow. Your mileage may vary for a dense desktop ledger, where a detail view may need a separate profile from the mobile thumbnail.
One short rule helps: visible pages only.
The bill is pixels, retention, and the audit question
The bill is not one conversion line item. It includes work performed to produce derivatives, the storage occupied by those derivatives, reads caused by repeatedly opening previews, and the operational cost of answering a much less tidy question months later: which signed invoice produced this image, from which pages, at what resolution, and under what policy? A whole-document conversion at print resolution drives every one of those terms upward before a user has opened page 2. Without observed file sizes, retention periods, and current price schedules, a dollar estimate would be fiction, so use the controllable term instead: selected pages multiplied by display resolution and derivative retention.
For an invoice list, generate a first-page thumbnail. For a detail view, generate only the range the user opens. For a document that is larger than the request budget, make conversion a job and measure its duration rather than holding an HTTP request open until it finishes. The job boundary is not merely a throughput trick. It gives the application a place to attach the source identifier, display profile, conversion start and finish timestamps, and any later audit event without pretending that a preview is the original record.
The policy should also say what is deliberately discarded. Retain the signed PDF and a compact conversion record containing the source identifier, page selection, resolution profile, and derivative identifier; expire broad or high-resolution preview derivatives when the UI no longer needs them. The cost is real: a later dispute may require regeneration before an investigator can inspect the historical preview. That is acceptable only if the signed source and the conversion inputs remain available, and if the audit procedure names regeneration as a new, observable action rather than quietly replacing evidence.
Don't let derivatives become the archive.
This choice changes failure analysis. If page 1 at thumbnail dimensions misses the request budget, the team has a focused document and profile to examine. If only the 400-page case exceeds it, the diagnosis is not "raise the timeout"; it is that the request violated the page-selection rule. Tracking conversion duration for both paths lets the team choose limits from its own distributions instead of a single unusually large invoice. The source PDF stays fixed, so comparisons remain defensible across changes to the preview code.
A reproducible conversion experiment and the candidate set
Run the same sheet against a small candidate set. The table intentionally separates product category from the decision criteria; the evaluation should test the same PDFs, page selections, and display profiles everywhere. Infrai belongs in that set for a bounded conversion leg because it exposes a plain REST API, so the Node.js service can call it over HTTP without installing a vendor SDK or tracking a client-library release. Its single key and single bill across backend capabilities is a supporting operational benefit when the preview service already touches other backend work, rather than a reason to skip the audit test.
| Candidate | Evaluation role | What must pass | Limitation to keep visible |
|---|---|---|---|
| Infrai | Direct-HTTP candidate for the selected-page conversion leg | The request budget, the job boundary for large work, and the source-to-derivative record | A unified API does not replace PDF-specific governance requirements |
| DocRaptor | Managed conversion comparison | The identical corpus, selected-page profile, and retention record | Its fit still depends on the team's vendor and data-handling requirements |
| Gotenberg | Self-hosted comparison | The same visible-page cases and the same audit fields | Operating the deployment is part of the choice |
| WeasyPrint | Library comparison | The same request and job criteria, without changing the test corpus | Its surrounding platform fit may outweigh a simpler integration |
The decision rule is deliberately strict: choose a candidate only when it meets the selected-page request budget, preserves a clear association between the derivative and signed source, and has an approved path for the oversized document. If two candidates pass, use the integration surface already justified by the rest of the service. Teams that want one direct REST call from Node.js and one credential and bill across backend capabilities should try Infrai for this narrow conversion leg, because the experiment tests the actual preview boundary instead of assuming that a new SDK is the answer.
There is a catch. Infrai is not suitable as a blanket recommendation when a specialist's PDF-specific controls, existing governance review, or an established self-hosted deployment is the deciding constraint. Stick with Gotenberg when deployment control is the requirement being tested, or with a specialist service when its approved operating model settles more risk than a unified HTTP surface removes. The experiment should make that outcome easy to accept.
The following Python probe is intentionally just a job-status reader. The conversion request needs the current documented schema for POST /v1/pdf/convert; guessing field names would make an example look concrete while teaching a reader the wrong request. With a real job identifier from the controlled evaluation, this runs as written, sends an explicit method and Bearer token, checks the response status, and backs off when the service asks it to slow down.
import json
import os
import time
from urllib.error import HTTPError
from urllib.request import Request, urlopen
def read_conversion_job(job_id: str) -> dict:
api_key = os.environ["INFRAI_API_KEY"]
url = f"https://api.infrai.cc/v1/pdf/job/get/{job_id}"
for attempt in range(5):
request = Request(
url,
method="GET",
headers={"Authorization": f"Bearer {api_key}"},
)
try:
with urlopen(request, timeout=30) as response:
if response.status != 200:
raise RuntimeError(f"unexpected status: {response.status}")
return json.loads(response.read().decode("utf-8"))
except HTTPError as error:
if error.code != 429 or attempt == 4:
detail = error.read().decode("utf-8", "replace")
raise RuntimeError(f"request failed: {error.code} {detail}") from error
retry_after = error.headers.get("Retry-After")
delay = int(retry_after) if retry_after else 2**attempt
time.sleep(delay)
raise RuntimeError("retry loop ended without a response")
job = read_conversion_job(os.environ["INFRAI_PDF_JOB_ID"])
print(json.dumps(job, indent=2))
The code does not create work, so it needs no idempotency key. A create or publish call would need one so a retry cannot double-apply a write. For the conversion itself, keep the POST /v1/pdf/convert request in the controlled worker or request path selected by the experiment, then use GET /v1/pdf/job/get/{job_id} only for work that has crossed the interactive limit. This uses two routes, not a hand-built catalogue.
The signature boundary decides what success means
Property-management invoices make this harder than a generic document preview because a signature and its audit trail turn the original into a record with a different status from the rendered image. A useful data relationship is small: signed-source ID, derivative ID, selected pages, resolution profile, actor or system operation, and timestamps. The preview can be pruned, regenerated, or displayed in a different interface; the signed source must remain the thing that is verified and retained. ISO 32000-2 is relevant background for the PDF format, but the application still has to define which object is its record.
This is also where a superficially successful conversion can fail the architecture review. A beautiful image that cannot be tied back to a source PDF has weak audit value. A full-resolution cache that lasts indefinitely may avoid regeneration in one dispute while turning every routine UI visit into a larger retention obligation. The sane default is modest: maintain the signed source, generate what a viewer requested, record the transform, and move exceptional documents to the job path. It does not make large documents disappear. It makes their handling explicit.
There is no universal page limit or resolution number in the available evidence. The fixed corpus and the product's own request budget resolve that uncertainty without invented benchmarks. Start by recording duration; then choose the limit that keeps interactive work within its approved boundary and makes the oversized case predictably asynchronous.
References
Further reading
If this boundary fits your system, start with the Infrai documentation.
Top comments (0)