Expose only the data needed by the frontend

With the astronomical rise of vibe coding and non-technical folks shipping software, it’s become apparent that now more than ever, we need to be aware of very insecure software flooding the market.

One of the mistakes I’m seeing now and again is developers exposing too much data (than what’s required in the frontend).

There’s no excuse for having a record’s updated_at, deleted_at and created_at fields in your API responses if the frontend doesn’t need them.

an image of the dev tools API response

Response from a blogging platform’s API showing unnecessary fields exposed

This is unfortunately a very common mistake that more often than not leads to security vulnerabilities and performance issues.

APIs are doorways to the data you’re storing and ultimately to your client’s business as well. They are a very detailed description of the client’s business processes, as well.

How does business xyz handle my data when I sign up? How do they process payments? How do they handle refunds?

For example, by looking at a certain fintech startup’s API responses, I was able to map at the different processes and verifications they have in place while handling a payment.

Take an instance of the image above (it is from a blogging platform’s API). The API response contains data in this format:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "id": 7,
  "title": "Sleep Your Way To The Top: How, Why And With Who",
  "content":"...full article content...",
  "slug": "sleep-your-way-to-the-top-how-why-and-with-who",
  "excerpt": "These are the confessions of a young executive who thought he was on the fast track to the top. Kumbe… ",
  "type": "feature",
  "status": "draft",
  "isPremium": false,
  "isFeatured": true,
  "publishedAt": null,
  "createdAt": "2025-07-18T10:30:19.641Z",
  "updatedAt": "2025-08-19T15:13:45.906Z",
  "authorId": 17,
  "categoryId": 1,
  "columnType": null,
  "tags": {},
  "author": {
    "id": 17,
    "slug": "tom-rwahwire",
    "user": {
      "id": 32,
      "fullName": "Tom Rwahwire",
      "username": "tom_rwahwire"
    }
  }
}

Suppose I’m a malicious user and I want to read a premium article without paying for it. What could I do?

I could get my authentication token, then try sending a PATCH request to the articles endpoint changing the isPremium field to true. I could keep doing this for all premium articles that I want to read.


We certainly don’t need to expose some fields for example createdAt, updatedAt, authorId, categoryId, etc. if they are not being used in the frontend.

How do we achieve this?

We can reduce the fields to the absolute minimum required by the frontend. For example:

1
2
3
4
5
6
7
8
9
{
  "title": "Sleep Your Way To The Top: How, Why And With Who",
  "slug": "sleep-your-way-to-the-top-how-why-and-with-who",
  "excerpt": "These are the confessions of a young executive who thought he was on the fast track to the top. Kumbe… ",
  "type": "feature",
  "isPremium": false,
  "isFeatured": true,
  "publishedAt": null,
}

This can be achieved by preparing response structures or using serialization libraries that allow you to define which fields to include or exclude in the API responses.

For example in Go, you can declare response structs that only include the necessary fields:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type ArticleResponse struct {
    ID          int       `json:"id"`
    Title       string    `json:"title"`
    Slug        string    `json:"slug"`
    Excerpt     string    `json:"excerpt"`
    Type        string    `json:"type"`
    IsPremium   bool      `json:"isPremium"`
    IsFeatured  bool      `json:"isFeatured"`
    PublishedAt *time.Time `json:"publishedAt,omitempty"`
}

Then map your data models to these response structs before sending them to the frontend.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Article represents the full article model from the database
type Article struct {
    ID          int
    Title       string
    Slug        string
    Excerpt     string
    Type        string
    IsPremium   bool
    IsFeatured  bool
    PublishedAt *time.Time
    CreatedAt   time.Time
    UpdatedAt   time.Time
    AuthorID    int
    CategoryID  int
}

// Assuming this is the an Article response from your database
 article := Article{
    ID:          7,
    Title:       "Sleep Your Way To The Top: How, Why And With Who",
    Slug:        "sleep-your-way-to-the-top-how-why-and-with-who",
    Excerpt:     "These are the confessions of a young executive who thought he was on the fast track to the top. Kumbe… ",
    Type:        "feature",
    IsPremium:   false,
    IsFeatured:  true,
    PublishedAt: nil,
    CreatedAt:   time.Date(2025, 7, 18, 10, 30, 19, 641000000, time.UTC),
    UpdatedAt:   time.Date(2025, 8, 19, 15, 13, 45, 906000000, time.UTC),
    AuthorID:    17,
    CategoryID:  1,
}
// Set content type headers and marshal the response and then, 
func GetArticleResponse(article Article) ArticleResponse {
    return ArticleResponse{
        Title:       article.Title,
        Slug:        article.Slug,
        Excerpt:     article.Excerpt,
        Type:        article.Type,
        IsPremium:   article.IsPremium,
        IsFeatured:  article.IsFeatured,
        PublishedAt: article.PublishedAt,
    }
}

This way, we can be sure that only the necessary data is sent to the frontend, reducing the risk of exposing sensitive information.

In any case, you have a software project that you would like us to discuss, reach out via hello@terraconsults.co

Resources