Skip to main content

On This Page

Stop Sending Nulls in Your API Responses

5 min read
Share

These are my own opinions based on building APIs that handle real traffic. This is not an industry standard, not a spec, and not a hill I'll die on. Take what's useful. Ignore what isn't.

If you’ve sat through backend architecture reviews, you’ve witnessed The Great Null Debate. Someone cleans up a JSON response. Someone else flags it as breaking or bad practice. The argument starts.

The debate centers on two tiny JSON values: null and empty arrays [].

Should your API explicitly state that a field exists but has no value (V1), or should it simply omit the field entirely (V2)?

V1: The Explicit Approach

{
  "id": "user_123",
  "name": null,
  "email": "[email protected]",
  "secondary_emails": [],
  "preferences": null
}

V2: The Sparse Approach

{
  "id": "user_123",
  "email": "[email protected]"
}

“Explicit is better than implicit” works for code logic. It fails for network transport.

My stance: Omit null fields and empty collections. V2 wins.

This is why, covering economics, semantics, and client reality.

Bandwidth Is Money

The strongest argument for sparse responses is cost. Not philosophy. Money.

A few extra bytes in a small internal API? Irrelevant. A few extra bytes multiplied across millions of mobile users? That’s real money leaving your budget.

The Scenario

A social media platform. Feed API returns 20 posts. Each post has 10 fields that are usually null (edited_at, location_tag, co_author, poll_data) and 3 arrays that are usually empty (tags, mentions, attachments).

A bloated post object (V1) might look like this:

// ... inside the timeline list
{
  "post_id": "p_987",
  "content": "Hello world!",
  "edited_at": null,
  "location_tag": null,
  "co_author": null,
  // ... 7 more null fields ...
  "tags": [],
  "mentions": [],
  "attachments": []
}

Roughly 150 bytes of useless data per post. Field names plus : null, syntax.

The Math

  • Waste per post: ~150 bytes
  • Posts per feed request: 20
  • Waste per request: 150 * 20 = 3,000 bytes (3KB)

3KB per request sounds negligible. Scale it.

  • Daily Active Users: 10 million
  • Feed refreshes per user per day: 5
  • Total requests per day: 50 million

Daily waste: 50,000,000 × 3KB = 150GB of useless data

Monthly waste: 150GB × 30 = 4,500GB (4.5TB)

The Cost

AWS charges for egress. Conservative estimate: $0.05/GB at volume.

4,500GB × $0.05 = $225,000/month

Configure your serializer to omit nulls and save $2.7 million annually. Plus reduced CPU on servers serializing it and clients parsing it.

This is not theoretical optimization. This is budget.

Null Is Ambiguous

Beyond cost, sending null creates semantic confusion.

In a database, NULL has meaning. In JSON over HTTP, it’s ambiguous. If an API returns "spouse_name": null, what does that mean?

  1. Not Applicable: The user is single.
  2. Unknown: The user hasn’t provided this information yet.
  3. Privacy/Security: The requestor doesn’t have permission to see this field.
  4. Partial Response: The backend simply didn’t join the necessary table for this specific query to save performance.

Sending null forces the client to guess intent.

Sparse Representation

Omit the key entirely. The meaning becomes clearer: For this request, this field does not exist.

If the client needs to know why, they check documentation or query a different endpoint. JSON payloads are for data transport, not schema definition.

Modern Clients Handle Absence Fine

Common objection: “But it helps frontend developers know what fields might exist so they don’t get undefined errors.”

This argument expired years ago.

TypeScript, Kotlin, Swift—modern languages handle missing data gracefully.

If you send nulls (V1), the client code often looks like defensive soup:

// The old way of handling V1 payloads
if (user.addresses && user.addresses !== null && user.addresses.length > 0) {
  // iterate addresses
}

If you omit keys (V2), modern clients use robust features like optional chaining:

// The modern way of handling V2 payloads
user.addresses?.forEach(addr => { ... });

If addresses is missing, the code doesn’t run. Cleaner. Safer. Relies on presence, not value.

Implementation

Sparse APIs are usually configuration, not code.

Java (Spring Boot / Jackson)

Jackson is Spring Boot’s default JSON library. Configure globally:

spring:
  jackson:
    default-property-inclusion: non_null

Or per-class if you want selective behavior:

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL) // Or NON_EMPTY to catch empty lists too
public class UserDTO {
    private String id;
    private String name;
    private List<String> addresses;
    // getters and setters
}

The Exception: PATCH Requests

One scenario where null matters: input payloads for PATCH requests.

GET retrieves state. PATCH modifies it.

If a client wants to explicitly erase a field (delete a secondary email), sending null signals that intent.

PATCH Request (Client sending to Server):

{
  "secondary_email": null
}

null means “set this to NULL in the database.” Omitting the key means “don’t change this field.”

But the response from that PATCH should still omit nulls.

Conclusion

Explicitly listing every possible field feels “safe.” It’s outdated practice that costs money and complicates clients.

Omit nulls and empty collections. APIs get faster, cheaper, cleaner. Bandwidth is finite. Respect it.

Continue reading

Next article

REST API Design: Beyond the Dogma

Related Content