You log an object before sending it to an API:
const user = {
id: 1,
name: "John",
email: undefined
};
console.log(user);
Output:
{
id: 1,
name: "John",
email: undefined
}
Everything looks fine.
Then you serialize it:
const json = JSON.stringify(user);
console.log(json);
Output:
{"id":1,"name":"John"}
The email field disappears completely.
No error.
No warning.
No indication that data was removed.
This behavior surprises many developers the first time they encounter it, especially when debugging API payloads or caching systems.
Problem
JSON.stringify() does not preserve undefined values.
Consider:
const payload = {
username: "john",
age: undefined,
city: undefined
};
console.log(JSON.stringify(payload));
Output:
{"username":"john"}
The properties with undefined values are omitted entirely.
This can create unexpected behavior when:
- Sending API requests
- Updating database records
- Caching objects
- Comparing payloads
- Generating audit logs
Why It Happens
The behavior comes from the JSON specification itself.
JSON supports only a limited set of data types:
- String
- Number
- Boolean
- Null
- Object
- Array
It does not support:
undefined- Functions
- Symbols
Because undefined is not a valid JSON value, JSON.stringify() excludes it from objects.
Example:
const data = {
name: "John",
age: undefined
};
console.log(JSON.stringify(data));
Result:
{"name":"John"}
The property is removed because JSON has no representation for undefined.
Example
Imagine a profile update API.
Frontend payload:
const updateData = {
firstName: "John",
lastName: undefined
};
Serialized request:
JSON.stringify(updateData);
Produces:
{
"firstName": "John"
}
The backend never receives lastName.
Now the server cannot distinguish between:
{
lastName: undefined
}
and:
{}
Both become:
{}
This distinction matters in partial update operations.
Production Ready Solution
Convert Undefined to Null
If you need fields to remain present, explicitly convert them.
const payload = {
name: "John",
email: undefined
};
const json = JSON.stringify(payload, (key, value) =>
value === undefined ? null : value
);
Output:
{
"name": "John",
"email": null
}
The field remains visible.
Create a Reusable Serializer
In larger codebases, repeating replacer functions becomes messy.
A helper works better:
function safeStringify(data) {
return JSON.stringify(data, (key, value) =>
value === undefined ? null : value
);
}
Usage:
const result = safeStringify(payload);
This keeps behavior consistent across services.
Define Clear API Semantics
In production APIs, it's important to define what each value means.
For example:
{
"email": null
}
might mean:
- Remove email
while
{}
might mean:
- Leave email unchanged
Without clear rules, clients and servers often interpret missing fields differently.
Validate Before Serialization
Sometimes the best solution is removing invalid data intentionally.
Example:
function removeUndefined(obj) {
return Object.fromEntries(
Object.entries(obj).filter(
([_, value]) => value !== undefined
)
);
}
Usage:
const cleaned = removeUndefined(data);
Now the omission is explicit instead of happening silently.
Common Mistakes
Assuming Undefined Becomes Null
Many developers expect:
{
email: undefined
}
to become:
{
"email": null
}
That never happens automatically.
The property is removed entirely.
Debugging the Wrong Object
This is common:
console.log(payload);
shows:
{
email: undefined
}
Then:
JSON.stringify(payload);
removes the field.
Developers often inspect the original object and assume the serialized payload contains the same data.
Always inspect the final JSON string.
Losing Fields During PATCH Requests
Suppose a frontend sends:
{
phone: undefined
}
After serialization:
{}
The backend may interpret this as:
- No changes requested
instead of:
- Remove phone number
This can lead to subtle update bugs.
Forgetting Nested Objects
The behavior applies recursively.
Example:
const user = {
profile: {
bio: undefined
}
};
console.log(JSON.stringify(user));
Output:
{
"profile": {}
}
Nested properties disappear as well.
Debugging Tips
When data seems to vanish during API calls, check the serialized payload first.
Log the Actual JSON
Instead of:
console.log(payload);
use:
console.log(JSON.stringify(payload));
This reveals exactly what will be transmitted.
Compare Before and After
console.log("Original:", payload);
console.log(
"Serialized:",
JSON.stringify(payload)
);
The difference is often immediately obvious.
Inspect Network Requests
In browser developer tools:
- Open Network tab
- Select request
- Check Request Payload
Many developers spend hours debugging backend code when the field was already removed by JSON.stringify() on the client.
Watch for ORM Updates
Some ORMs treat:
null
and
undefined
very differently.
Examples include:
- MongoDB drivers
- Prisma
- Sequelize
- TypeORM
Verify how your persistence layer handles missing properties versus explicit null values.
Performance Considerations
The default JSON.stringify() implementation is highly optimized.
Using a replacer function:
JSON.stringify(data, replacer)
introduces additional processing because every property is inspected.
For normal API payloads, the overhead is negligible.
For very large objects:
- Cache transformed objects when possible
- Avoid unnecessary serialization cycles
- Benchmark large datasets before introducing custom replacers everywhere
In most production systems, correctness matters far more than the tiny performance cost.
Final Thoughts
JSON.stringify() is not removing undefined values by accident.
It's following the JSON specification.
The key thing to remember is:
{
email: undefined
}
becomes:
{}
not:
{
"email": null
}
Whenever a field must remain visible after serialization, convert undefined to a valid JSON value such as null.
Understanding this behavior can save a surprising amount of debugging time, especially when working with APIs, databases, caching layers, and distributed systems where missing fields and null values often have very different meanings.
For more JavaScript and Node.js debugging articles based on real production issues, I occasionally publish deep dives on NileshBlog and TechNilesh when a bug is interesting enough to document.
Top comments (0)