DTO vs Entity in REST APIs
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
A JPA entity represents data managed by persistence. It has an identity and mapped state, participates in a lifecycle in a persistence context, and can declare relationships to other entities. A DTO, or Data Transfer Object, represents the data for one request or response. The classes may share fields, but an entity answers database needs while a DTO answers one public API use case.
A create endpoint can accept record CreateUserRequest(String displayName, String email) {}. Because id, role, passwordHash, and version are absent, JSON binding cannot set them through this request type. The DTO can also use names and validation rules that match the client contract instead of exposing persistence details.
The entity may contain a generated ID, password hash, audit timestamps, an optimistic-lock version, and mapped organization relationships. Those fields exist for storage and domain work, not automatically for JSON. Binding a request directly to the entity can allow over-posting, while returning it can expose private data or make serialization touch lazy relationships.
After the service finishes, mapping can return new UserResponse(user.id, user.displayName). That response has no path to the password hash or persistence version. It also gives related data a deliberate shape, such as a small organization summary or link, instead of walking a bidirectional entity graph and possibly causing recursion or extra database queries.
Separate DTOs add classes and mapping code. Manual mapping is often clearest for a small contract, while a generated mapper can help when many fields repeat, provided its output is reviewed and tested. The benefit is independent change: a table relationship can be refactored without silently changing JSON, and a new API field does not have to become persistent state.
DTO vs Entity in REST APIs
Direct Answer
A JPA entity represents persistent data with identity, mapped state, a persistence lifecycle, and any declared relationships, whereas a DTO represents the data accepted or returned by one API use case. Separate DTOs stop clients from setting internal fields or seeing sensitive ones and let the API contract change without exposing every persistence-model change.
Deep Dive & Architecture
An entity is designed around consistency and persistence operations. It may have lazy relationships, optimistic-lock versions, surrogate keys, and fields needed only for auditing. An API representation is designed around a client's task and compatibility. Publishing the entity turns every persistence refactor into a potential API change.
Request DTOs create an allow-list. A create request can omit server-owned fields entirely, while an update request can model exactly which changes are permitted. This is stronger than accepting an entity and hoping annotations hide enough fields. Validation messages also attach naturally to the request vocabulary the client sent.
Response DTOs bound the object graph. Instead of serializing Order -> Customer -> Orders -> Customer, a response can include a customer link or compact summary. The service loads the data the use case needs, maps it inside the correct transaction, and avoids accidental N+1 queries caused by a serializer touching lazy properties.
Mapping can be manual, generated, or projection-based. Manual mapping is clearest for small contracts. Generated mapping helps many similar fields but should fail visibly when fields change. Regardless of tool, contract tests should assert JSON field names, null behavior, and sensitive-field absence.
Code Example
import jakarta.persistence.*;
@Entity
class UserEntity {
@Id long id;
String displayName;
String email;
String passwordHash;
@Version long version;
protected UserEntity() {}
}
final class UserMappingExample {
record CreateUserRequest(String displayName, String email) {}
record UserResponse(long id, String displayName) {}
static UserResponse toResponse(UserEntity user) {
return new UserResponse(user.id, user.displayName);
}
}
The response has no path to passwordHash or the persistence version. A separate create mapper can apply server-owned defaults rather than binding them from client JSON.
Check your understanding
Answer these without looking back at the article.
- How can direct entity binding cause an over-posting vulnerability?
- Why can entity serialization trigger an N+1 query problem?
- What trade-off does explicit DTO mapping introduce?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.