Private betav0.1.0
Docs
Format developer documentation

Java

Generate PDFs from Java applications using the Format API.

Format doesn't have a dedicated Java SDK yet, so this page outlines our recommend approach to use Format within a Java environment.

Useful

Want a dedicated SDK?

We're exploring native SDKs for additional platforms. If this is important for your team, reach out to us.

How it works

This is our recommended workflow for Java users.

Design

Create your PDF templates locally using Format Studio (React, Vue, or HTML).

Compile

Compile them down to a renderer bundle and assets zip using format compile. This can be done locally or in CI.

Deploy a render service

Deploy a lightweight Node.js service alongside your application. This service takes your data, renders the HTML from the compiled bundle, and calls the Format API to return the final PDF.

Call the render service

Your application sends data to the render service and receives a PDF back. No need to interact with the Format API directly.

The render service

Your compiled Format templates are JavaScript modules. The render service wraps them in a small HTTP server that accepts data, renders the HTML, and returns the PDF. You can use any server or serverless infrastructure that supports a Node runtime.

Useful

If your PDF has no dynamic data, you don't need a render service. You can call render() once after compile, save the resulting HTML to a file, and have your application send that static HTML directly to the Format API.

Here is a standalone example using Hono:

render-service.js
import { invoice } from './_generated'
import { FormatClient } from '@format.dev/client'
import { serve } from '@hono/node-server'
import { Hono } from 'hono'

const format = new FormatClient()
const app = new Hono()

app.post('/render/invoice', async (c) => {
  const data = await c.req.json()
  const doc = await invoice.render(data)
  const response = await format.pdf(doc)

  return new Response(response.body, {
    headers: {
      'Content-Type': 'application/pdf',
      'Content-Disposition': `attachment; filename="${response.filename ?? 'document.pdf'}"`,
    },
  })
})

serve(app, { port: 3100 })

The _generated directory (produced by format compile) is deployed alongside this service. It contains the renderer bundles and asset zips. Your application does not need access to these files.

Calling from Java

Your Java application sends the document data as JSON and receives a PDF stream back. The render service can run locally alongside your application or as a separate service in production (a different container, serverless function, etc.).

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;

String renderServiceUrl = System.getenv().getOrDefault("RENDER_SERVICE_URL", "http://localhost:3100");

String invoiceData = """
    { "customerName": "Ada Lovelace", "invoiceNo": "INV-001" }
    """;

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(renderServiceUrl + "/render/invoice"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(invoiceData))
    .build();

HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());

if (response.statusCode() == 200) {
    Files.write(Path.of("./output/invoice.pdf"), response.body());
}

Calling the API directly

If you prefer to skip the render service and call the Format API directly from Java, you can do so with the built-in HttpClient.

The Format API accepts a POST request with multipart form data and returns a PDF binary stream.

POST https://api.format.dev/v1/render
Authorization: Bearer {FORMAT_API_KEY}
Content-Type: multipart/form-data
FieldTypeDescription
htmlstringThe rendered HTML output from your compiled template
assetsbinary (optional)A ZIP file containing images, fonts, and static resources
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;

String apiKey = System.getenv("FORMAT_API_KEY");
String boundary = UUID.randomUUID().toString();

byte[] assetsBytes = Files.readAllBytes(Path.of("./assets.zip"));

String body = "--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"html\"\r\n\r\n"
    + html + "\r\n"
    + "--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"assets\"; filename=\"assets.zip\"\r\n"
    + "Content-Type: application/zip\r\n\r\n";

byte[] prefix = body.getBytes();
byte[] suffix = ("\r\n--" + boundary + "--\r\n").getBytes();

byte[] requestBody = new byte[prefix.length + assetsBytes.length + suffix.length];
System.arraycopy(prefix, 0, requestBody, 0, prefix.length);
System.arraycopy(assetsBytes, 0, requestBody, prefix.length, assetsBytes.length);
System.arraycopy(suffix, 0, requestBody, prefix.length + assetsBytes.length, suffix.length);

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.format.dev/v1/render"))
    .header("Authorization", "Bearer " + apiKey)
    .header("Content-Type", "multipart/form-data; boundary=" + boundary)
    .POST(HttpRequest.BodyPublishers.ofByteArray(requestBody))
    .build();

HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());

if (response.statusCode() == 200) {
    Files.write(Path.of("./output/invoice.pdf"), response.body());
}

This approach requires you to produce the HTML and assets yourself. See the API client reference for full details.

Was this page helpful?