Expressive REST Resources with Java Records and Jakarta REST

Jakarta EE
No Image

Modern web applications often adopt a layered architecture to separate concerns and improve maintainability. In this paradigm, your REST API acts as a communication layer, exposing resources to clients while interacting with the deeper layers of your application. Leveraging Java Records as Data Transfer Objects (DTOs) in this context helps to simplify the design of your REST resources, making them more expressive and easier to work with.

Why Not Expose Database Entities Directly?

While it might seem tempting to directly expose your database entities (often modelled with Jakarta Persistence (JPA)) as REST resources, there are several compelling reasons to avoid this approach:

  • Tight Coupling: Directly exposing entities binds your API too closely to the database schema. Any changes to the database structure could force changes in your API, breaking backward compatibility and potentially impacting clients.
  • Security Risks: Entities often contain fields that shouldn’t be directly visible to API consumers. Exposing them increases the risk of inadvertent data leakage.
  • Data Transformation: Your API might need to present data in a format that differs from the underlying database representation, e.g., calculated fields and merging data from multiple entities.
  • Over-fetching: Entities often model relationships to other entities. Exposing them directly can lead to returning more data than the client actually needs, impacting performance.

DTOs to the Rescue

DTOs introduce a layer of separation between your database entities and the API layer. They are simple data containers tailored specifically to the data your API needs to send and receive.

Benefits of DTOs:

  • Decoupling: Your API contract becomes independent of your database implementation, supporting for flexibility on both ends.
  • Security: You expose only the necessary data, reducing security risks.
  • Customization: You can design DTOs to match API requirements or specific views.
  • Performance: DTOs allow you to fetch and transfer only the data needed by the client.

Java Records As DTOs

Java Records, first introduced in Java 14, are an excellent fit when defining DTOs. They offer:

  • Concise Syntax: You can eliminate the boilerplate code often associated with traditional DTO classes.
  • Immutability: By default, records are immutable, ensuring data integrity across layers.
  • Readability: Records make the intent of a DTO immediately clear–they are purely data holders.

Example

Let’s imagine a Book entity in our database with additional relationships:

@Entity

public class Book {

   @Id
   private int id;

    private String title; 

   private String author;
   @ManyToOne 
   private Publisher publisher;
    // ... other fields, relationships

}

Now, a simplified DTO using a Java Record for our REST layer would look like:

public record BookDTO(int id, String title, String authorName) {} 

Notice how the BookDTO only includes the crucial information needed for most API interactions, hiding the relationship to the Publisher. Our REST endpoint can then consume and return a BookDTO as follows:

    @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
  public BookDTO saveBook(@Valid BookDTO bookDTO) {

      return persistenceService.saveBook(bookDTO);

    }

    @GET

    @Path("{title}")

    public List<BookDTO> filterBooks(@NotEmpty @PathParam("title") String title) {

             return queryService.filterBooks(title);

    }

In Practice

Within your REST resource classes, you’d likely use a mapping library (like MapStruct) to convert between your entities and their corresponding DTOs, managing the data flow between the REST layer and your service/persistence layers in an automated and transparent way. 

Conclusion

By using DTOs and leveraging the conciseness of Java Records, you can create REST APIs that are more secure, maintainable and expressive. This separation of concerns gives you the flexibility to evolve your database schema and your API contract independently. Happy Coding!

Comments (1)

Post a comment

Your email address will not be published. Required fields are marked *

Payara needs the contact information you provide to us to contact you about our products and services. You may unsubscribe from these communications at any time. For information on how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy, please review our Legal & Privacy Policy.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. Vedran C

    Very well to understand.

Related Posts

Cut Jakarta EE Startup Times from Seconds to Milliseconds with CRaC 8 minutes
Jakarta EE

Cut Jakarta EE Startup Times from Seconds to Milliseconds with CRaC 

Jakarta EE applications can take anywhere from several seconds to over a minute to start, depending on their size […]

Stacked copies of the Payara developer guide “Zero Trust Architecture with Jakarta EE and MicroProfile” on an orange background, showing the dark blue cover design with the Payara logo and a laptop illustration featuring a shield and padlock icon. 4 minutes
Jakarta EE

Implementing Zero Trust Security with Jakarta EE: A Practical Guide

Zero Trust security has moved from buzzword to necessity. The principle is simple: never trust, always verify. But implementing […]

Blue background with coral and fish. Left text: 'MONTHLY CATCH'. Right: laptop screen with tech tabs and Payara Community logo. 4 minutes
Community

The Payara Monthly Catch – December 2025

As we kick off the new year, this January edition of The Monthly Catch looks back at everything that […]