DEV Community

Cover image for Why Exposing Persistence Entities in APIs Is a Dangerous Shortcut
Krishna Nayak
Krishna Nayak

Posted on

Why Exposing Persistence Entities in APIs Is a Dangerous Shortcut

I recently revisited one of my old backend projects.

Everything was working fine. No errors. No failing APIs.

But while reading the code, one design decision immediately stood out — I had exposed persistence entities directly in API requests and responses.

At the time, it felt like a smart move. Looking back now, I realize it was a shortcut with hidden costs.


The Shortcut That Seemed Like a Smart Move

In the initial version, my controller looked something like this:
(Here, BookItem is a persistence entity class used for creating book records.)

@PostMapping("/books")
public BookItem create(@RequestBody BookItem bookItem) {
    return bookService.save(bookItem);
}
Enter fullscreen mode Exit fullscreen mode

Why did this feel right?

  • The entity already had all the fields
  • No extra classes to write
  • Faster development

DTOs felt like unnecessary boilerplate. For a small project, this approach worked — and that’s exactly why it was misleading.


The First Problem: Clients Can Send Too Much

When an entity is used directly as a request body, every field becomes writable.

client-request

This means a client can:

  • Set database IDs manually
  • Modify fields they should never control
  • Send values meant only for internal logic

The framework doesn’t know intent — it simply maps fields. Without a boundary, the API trusts the client far too much.


The Second Problem: Leaking Sensitive Data

Entities are designed for persistence, not for exposure.
When returned directly in API responses, they often include:

  • Internal flags
  • Audit timestamps
  • Fields irrelevant or unsafe for clients

client-response

Once clients start consuming these fields, they become part of the public contract — even if you never intended them to be.


The Real Cost: Change Becomes Dangerous ‼️

This is the most critical problem — and the one that usually appears too late.

A Realistic Example

Initial Entity

@Entity
public class BookItem {
    @Id
    private String id;
    private String title;
    private String author;
    private int pages;
}
Enter fullscreen mode Exit fullscreen mode

API Response

{
  "id": "123",
  "title": "Clean Code",
  "author": "Robert Martin",
  "pages": 464
}
Enter fullscreen mode Exit fullscreen mode

Clients build their logic around this response.

Later: A Normal Business Change

You decide that pages is inaccurate and replace it with wordCount.

@Entity
public class BookItem {
    @Id
    private String id;
    private String title;
    private String author;
    private int wordCount;
}
Enter fullscreen mode Exit fullscreen mode

You didn’t touch the controller. You didn’t change the API intentionally.

But the response now becomes:

{
  "id": "123",
  "title": "Clean Code",
  "author": "Robert Martin",
  "wordCount": 150000
}
Enter fullscreen mode Exit fullscreen mode

What Just Happened?

  • pages disappeared
  • wordCount appeared
  • Clients break
  • Frontend crashes
  • Mobile apps fail

Business Change, Prod Fail

This happened because the entity was acting as the API.
Every internal refactor became a breaking change.
That is why this design is dangerous.


Why DTOs Solve This Problem

DTOs create a stable boundary between internal models and external consumers.

DTO solution

With DTOs:

  • Clients can send only allowed fields
  • Responses expose only safe data
  • Entities can change freely
  • APIs remain stable

Final Thought

Using entities directly in APIs often feels productive at first.

But the real cost appears later — when requirements change and refactoring becomes risky.

An entity is not an API.

DTOs may add a few extra classes, but they protect your system from silent breakage and future pain.

Top comments (0)