null vs undefined — The Core Difference
JSON is a language-independent format. It recognizes null as a valid value representing "no value." JSON does not recognize undefined — it is a JavaScript-only concept that does not exist in the JSON specification (RFC 8259).
JSON.parse("null"); // Returns null (valid)
JSON.parse("undefined"); // SyntaxError: Unexpected token u
JSON.parse('"undefined"'); // Returns the string "undefined" (valid)This asymmetry causes bugs that are subtle and hard to diagnose, especially in API communication where the difference between a missing field and a null field has semantic meaning.
What JSON.stringify Does with undefined
This is where most bugs originate. JSON.stringify silently drops any object property whose value is undefined:
const user = {
name: "Ravi",
middleName: undefined, // dropped silently
age: 28,
role: null, // kept as null
};
JSON.stringify(user);
// '{"name":"Ravi","age":28,"role":null}'
// middleName is gone — no error, no warningIn arrays, undefined becomes null (because arrays are index-ordered and a missing element changes the structure):
JSON.stringify([1, undefined, 3, null]);
// '[1,null,3,null]' — undefined → null, null stays nullOther values that JSON.stringify converts silently:
- NaN → null
- Infinity → null
- -Infinity → null
- Function values → dropped (like undefined)
- Symbol values → dropped
Practical Bug Patterns
Bug 1: Optional field becomes silently absent
const body = {
userId: user.id,
referralCode: promoCode || undefined, // dropped if falsy!
};
JSON.stringify(body);
// If promoCode is "" (empty string) or null:
// '{"userId":42}' — referralCode missing entirely
// API expected: '{"userId":42,"referralCode":null}'Fix — use null for absent optional fields:
const body = {
userId: user.id,
referralCode: promoCode || null, // always present
};Bug 2: Conditional spread creates inconsistent shape
const body = {
userId: user.id,
...(promoCode ? { referralCode: promoCode } : {}), // key sometimes missing
};
// Could send {"userId":42} or {"userId":42,"referralCode":"SAVE20"}
// API may require the field always presentBug 3: Accessing missing nested fields
const city = data.user.address.city;
// TypeError: Cannot read properties of undefined (reading 'city')
// if data.user.address is null or undefinedFix — optional chaining with nullish coalescing:
const city = data?.user?.address?.city ?? "Unknown";
const firstTag = data?.tags?.[0] ?? null;Bug 4: Loose equality traps
const x = null;
x === null; // true — strict: null is null
x === undefined; // false — strict: they are different
x == null; // true — loose: null == undefined is true
x == undefined; // true — same as aboveRule: use === null when you specifically mean "JSON null." Use == null when you mean "absent or unset in any way."
Safe API Response Access Pattern
// Option 1: Optional chaining (ES2020+) — preferred
const city = response?.data?.user?.address?.city ?? "Unknown";
const accountId = response?.data?.account?.id ?? null;
// Option 2: Utility for dynamic paths
function getPath(obj, path, fallback = null) {
return path.split(".").reduce(
(acc, key) => (acc != null && acc !== undefined ? acc[key] : undefined),
obj
) ?? fallback;
}
const email = getPath(apiResponse, "user.contact.email", "");Converting undefined to null Before Serializing
When you must serialize objects that might contain undefined values:
function undefinedToNull(value) {
return JSON.parse(
JSON.stringify(value, (_, v) => (v === undefined ? null : v))
);
}
const safe = undefinedToNull({
name: "Ravi",
score: undefined,
address: { city: "Surat", zip: undefined },
});
// { name: "Ravi", score: null, address: { city: "Surat", zip: null } }TypeScript type guard to distinguish null from undefined:
function isPresent<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
const items = [1, null, 2, undefined, 3];
const valid = items.filter(isPresent); // [1, 2, 3], typed as number[]Null Handling in Other Languages
Python — None is Python's equivalent of JSON null:
import json
data = json.loads('{"name": "Ravi", "score": null}')
data["score"] is None # True
data["score"] == None # TrueGo — use pointer types for nullable fields:
type User struct {
Name string `json:"name"`
MiddleName *string `json:"middle_name"` // nil = JSON null or absent
}API Design Recommendations for Null vs Absent
| Situation | Recommendation |
|---|---|
| Field has no value for this record | Use null — keeps the schema consistent |
| Field does not exist for this resource type | Omit it entirely |
| Optional filter in a request body | Accept both null and absence — treat them the same |
| Patch endpoint (PATCH) | null = clear the field; absent = do not change |
| Creation endpoint (POST) | Require all mandatory fields; default optional ones to null |
Use JSONKit's JSON Formatter to inspect API responses and verify that expected fields are present and not silently dropped.