Skip to main content

Command Palette

Search for a command to run...

Return PDF file in a Micronaut Controller

Updated
Return PDF file in a Micronaut Controller

Date: 2025-05-12

Micronaut: Generating and Serving PDF Files from a REST Endpoint

Micronaut, a lightweight and versatile framework built for the Java Virtual Machine (JVM), excels at creating scalable microservices and cloud-native applications. Its efficiency stems from its compile-time approach to dependency injection and aspect-oriented programming, unlike traditional frameworks that rely heavily on runtime processes. This results in significantly faster startup times and reduced memory consumption, making it ideal for resource-constrained environments like serverless functions on platforms such as AWS Lambda or Google Cloud Functions. Micronaut's support for Java, Kotlin, and Groovy offers developers flexibility in their choice of language. Furthermore, its seamless integration with GraalVM allows for ahead-of-time (AOT) compilation into native executables, leading to even greater performance improvements.

One common requirement in business applications is the ability to generate and serve PDF documents, often for reports, invoices, or receipts. This functionality can be easily integrated into a Micronaut application by creating a REST API endpoint that returns a PDF file. This involves several steps: setting up necessary dependencies, creating a service to handle PDF generation, and building a controller to manage the API endpoint.

The process begins with defining the required dependencies. The application would need to include a library capable of creating PDF files. The original example mentions OpenPDF, an open-source alternative to more commercial options. This library, along with any other supporting libraries, would be declared as dependencies in the application's build configuration (e.g., using Gradle or Maven). These dependencies provide the tools needed to generate a PDF document programmatically.

Next, a dedicated service is created to handle the logic for PDF generation. This approach promotes modularity and maintainability. The service is designed as a reusable component, separate from the controller handling the HTTP requests. In the context of this example, a service called PdfService is introduced. This service contains a function, generatePdf, responsible for producing the PDF content. This function uses the OpenPDF library to create a PDF document in memory. A temporary storage area, like a ByteArrayOutputStream, is utilized to hold the PDF data as it is being generated. This prevents the need for temporary files on the disk, improving performance and cleaning up after itself. OpenPDF's Document object represents the PDF being constructed. The PdfWriter component is responsible for writing the document's content into the ByteArrayOutputStream. Within this function, elements like text, paragraphs, or images (not included in the minimal example) can be added to the PDF. Once the PDF is complete, the ByteArrayOutputStream is converted into a byte array, which is suitable for sending as an HTTP response.

The final piece is a Micronaut controller that exposes the PDF generation functionality as a REST endpoint. This controller, referred to as PdfController, is annotated to indicate its role. The annotation specifies the path at which the endpoint will be accessible (in this case, /pdf). Importantly, dependency injection is utilized to access an instance of the PdfService. This allows the controller to easily leverage the PDF generation logic without needing to know the specifics of how the PDF is created. A function within the controller, such as getPdf, handles incoming HTTP GET requests to the endpoint. This function invokes the generatePdf method of the PdfService to obtain the PDF data as a byte array. The controller then constructs an HTTP response with a status code of 200 (OK), sets the content type to application/pdf, and includes the generated PDF in the response body. Crucially, a Content-Disposition header is set to instruct the client (usually a web browser) to prompt the user to save the file, providing a suggested filename like example.pdf. This provides a seamless download experience for the user. Error handling is incorporated to gracefully manage any exceptions that might arise during PDF generation, returning an appropriate server error response if necessary.

This architecture offers several advantages. Separating the PDF generation logic into its own service results in a cleaner, more maintainable codebase. The use of OpenPDF, an open-source library, provides a cost-effective and flexible solution. This modular design allows for easier expansion and modification of PDF generation capabilities. Future enhancements could involve integrating dynamic data, tables, images, or charts into the PDF documents. The design readily scales to handle more complex PDF generation requirements. Moreover, the use of in-memory PDF generation avoids the overhead and potential issues of temporary file management on disk.

The complete process from user request to PDF delivery involves a client making a request to the designated endpoint. The Micronaut controller receives this request, calls the dedicated PdfService to generate the PDF, and finally sends back the generated PDF file as a response to the client, packaged with the appropriate HTTP headers to ensure correct handling by the client's browser or application. The client receives the PDF and can save or view it as intended. This entire process illustrates Micronaut's strengths in building efficient, scalable, and maintainable microservices for modern business applications.

Read more

More from this blog

The Engineering Orbit

1174 posts

The Engineering Orbit shares expert insights, tutorials, and articles on the latest in engineering and tech to empower professionals and enthusiasts in their journey towards innovation.