Course topics

By WebNest Studio Engineering Team

Spring Boot Tutorial

DTOs in Spring Boot (Data Transfer Objects)

A DTO (Data Transfer Object) is a plain object used to carry data between layers of an application — most commonly between the HTTP layer and the outside world — without exposing your internal domain/entity model directly. In a Spring Boot REST API, this means your @RestController methods should typically accept and return DTOs, not JPA @Entity classes.

Why Not Just Return the Entity?

It's tempting to return a JPA entity directly from a controller since Jackson will happily serialize it, but this couples your public API contract to your database schema. Any column you add, rename, or remove on the entity immediately (and often unintentionally) changes your API response. Entities can also carry lazy-loaded associations that throw LazyInitializationException when serialized outside a transaction, and they may expose fields (password hashes, internal flags, audit columns) that should never leave the server.

  • Entities model persistence (tables, relationships, JPA annotations); DTOs model the API contract (exactly what a client should see or send).
  • DTOs let you shape a response differently than storage — combining fields from multiple entities, hiding sensitive data, or renaming fields for the client.
  • Changing your database schema does not have to break your API if DTOs sit between them.

Request DTOs vs Response DTOs

It's common (and good practice) to use separate DTO classes for incoming requests and outgoing responses, since the fields a client is allowed to send (e.g. name, email) often differ from what a client should receive back (e.g. id, name, email, createdAt — but never a password). Mapping between entity and DTO is usually done manually in a service/mapper class, or with a library like MapStruct for larger projects.

Example

Separate request/response DTOs mapped to and from a JPA entity

Java
// Entity - persistence model, never exposed directly
@Entity
public class User {
    @Id @GeneratedValue
    private Long id;
    private String name;
    private String email;
    private String passwordHash; // must never leave the server
    private LocalDateTime createdAt;
    // getters/setters omitted
}

// Request DTO - only what a client is allowed to submit
public record CreateUserRequest(String name, String email, String password) {}

// Response DTO - only what a client should see
public record UserResponse(Long id, String name, String email, LocalDateTime createdAt) {}

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService service;
    public UserController(UserService service) { this.service = service; }

    @PostMapping
    public ResponseEntity<UserResponse> create(@RequestBody CreateUserRequest request) {
        User saved = service.createUser(request);
        UserResponse body = new UserResponse(saved.getId(), saved.getName(),
                saved.getEmail(), saved.getCreatedAt());
        return ResponseEntity.status(HttpStatus.CREATED).body(body);
    }
}
Output
POST /api/users {"name":"Asha","email":"asha@webnest.com","password":"secret123"}
-> 201 Created {"id":1,"name":"Asha","email":"asha@webnest.com","createdAt":"2026-09-21T10:15:30"}

Common Mistakes

  • Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations.
  • Using a single DTO class for both requests and responses, which forces awkward compromises (e.g. an id field that must be ignored on create).
  • Putting JPA annotations (@Entity, @Id) on what is meant to be a DTO, blurring the line between persistence and API contract.
  • Manually copying every field with dozens of getter/setter calls in large projects instead of adopting a mapping library once the entity graph grows.

Exercise

A teammate is about to make this mistake: Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations. Use Why Not Just Return the Entity? to review the decision. Write a corrected implementation or design sketch that follows DTOs decouple the API contract from the database schema and internal domain model., then name one edge case you would check.

Show hints
  • Start with Why Not Just Return the Entity? and compare your approach with Separate request/response DTOs mapped to and from a JPA entity. Identify the decision or behavior the lesson recommends.
  • Walk through your scenario step by step. Confirm that it follows DTOs decouple the API contract from the database schema and internal domain model. and that it would catch the failure described by Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations..

Quick quiz

1. Which choice best describes the recommended approach in DTOs in Spring Boot (Data Transfer Objects)?

  • A. Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations.
  • B. Using a single DTO class for both requests and responses, which forces awkward compromises (e.g. an id field that must be ignored on create).
  • C. DTOs decouple the API contract from the database schema and internal domain model.
Show answer

C. This is the recommended approach: DTOs decouple the API contract from the database schema and internal domain model. Avoid: Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations.

Interview questions

Why does this principle matter in DTOs in Spring Boot (Data Transfer Objects): DTOs decouple the API contract from the database schema and internal domain model.

Show answer

DTOs decouple the API contract from the database schema and internal domain model. Apply this in Why Not Just Return the Entity?; the example Separate request/response DTOs mapped to and from a JPA entity shows one way to do it. Avoid Returning JPA entities directly from controllers, leaking internal fields and risking LazyInitializationException on unfetched associations. and explain how your implementation or design prevents that failure.

Key Points to Remember

  • DTOs decouple the API contract from the database schema and internal domain model.
  • Never return JPA entities directly from a @RestController — map them to response DTOs first.
  • Separate request and response DTOs let each side expose exactly the fields it should.
  • Java records are a concise, idiomatic way to write immutable DTOs.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering