Migration guides/server-side PDF

From server PDF to WASM

Move PDF operations from a server endpoint to a WebAssembly module running in the browser. PDFluent compiles to WASM. No server round-trips for basic PDF operations.

Migrating from server-side PDF to PDFluent. Install with cargo add pdfluent

Migration steps

1

Identify which PDF operations can move to the client

Not every PDF operation belongs in the browser. Operations that work well in WASM: form filling, text extraction, annotation, metadata editing, and basic PDF creation. Operations that should stay server-side: high-security signing, server-only data injection, and compliance workflows requiring an audit trail.

server-side PDF (before)
// Current: every PDF operation hits your server
// POST /api/pdf/fill-form
// POST /api/pdf/extract-text
// POST /api/pdf/add-annotation
// POST /api/pdf/merge
//
// Round-trip latency: 200-800ms per operation
// Server load: scales with user count
// Offline use: impossible
PDFluent (after)
// Target: common operations run in the browser
// WASM module loaded once, runs locally
//
// Round-trip latency: 0ms (runs in tab)
// Server load: only for signing and compliance
// Offline use: fully supported
2

Build PDFluent as a WASM target

PDFluent compiles to WebAssembly using wasm-pack. The output is a ~6 MB .wasm file (~2 MB Brotli-compressed over the wire) plus a JavaScript glue module. Add the wasm target to your Rust toolchain and run wasm-pack build to produce an npm-compatible package.

server-side PDF (before)
// Server endpoint (Node.js / Express example)
app.post('/api/pdf/fill-form', async (req, res) => {
    const { pdfBytes, fields } = req.body;
    // Server receives PDF bytes from client,
    // processes them, returns filled PDF.
    // Every user action requires a network round-trip.
    const filled = await fillFormOnServer(pdfBytes, fields);
    res.send(filled);
});
PDFluent (after)
# Build PDFluent for WASM
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
wasm-pack build --target web --release

# Output: pkg/pdfluent_bg.wasm (~6 MB, ~2 MB Brotli-compressed)
#         pkg/pdfluent.js      (glue module)
3

Load the WASM module and replace the server calls

Import the wasm-pack output as an ES module in your frontend code. Initialize the WASM binary once on page load, then call PDFluent operations directly in the browser. Replace fetch() calls to your server endpoint with direct WASM calls on the PDF ArrayBuffer.

server-side PDF (before)
// Before: round-trip to server
async function fillForm(pdfBytes, fields) {
    const response = await fetch('/api/pdf/fill-form', {
        method: 'POST',
        body: JSON.stringify({ pdfBytes: Array.from(pdfBytes), fields }),
        headers: { 'Content-Type': 'application/json' },
    });
    return new Uint8Array(await response.arrayBuffer());
}
PDFluent (after)
// After: runs in the browser via WASM
import init, { fill_form_fields } from './pkg/pdfluent.js';

await init(); // load .wasm once at startup

async function fillForm(pdfBytes, fields) {
    // Runs locally — no network, no server
    return fill_form_fields(pdfBytes, fields);
}

Things to watch out for

  • !The WASM binary is ~6 MB uncompressed (~2 MB over the wire with Brotli). Load it once and cache it with a Service Worker for offline support.
  • !PDF signing with a server-held private key must stay server-side. WASM is appropriate for user-facing, non-sensitive operations.
  • !WASM runs in a single thread by default in browsers. For large PDFs, consider Web Workers to avoid blocking the main thread.
  • !If users upload PDFs to your server for processing, shifting to WASM also reduces GDPR surface area — the PDF bytes never leave the browser.

Frequently asked questions