| |

REST API 06 ๐ŸŒ JSON Data Serialization and Payload Design

โฑ๏ธ Estimated read: 13 min | |

JSON is the default payload format for REST APIs not because it is the best possible format in every dimension, but because it hits the sweet spot that no other format matches: native data types, human readability, ubiquitous tooling, and a natural fit with JavaScript clients . The serialization stepโ€”converting your application’s objects into JSON and backโ€”is usually handled by a library, and that library’s defaults determine much of your API’s shape. The payload design stepโ€”deciding what the JSON actually looks likeโ€”is where the API’s contract is defined, and it is where most of the long-term maintenance pain originates.

The distinction between serialization and payload design is practical. Serialization is mechanical: a User object goes in, JSON comes out. Payload design is architectural: does the JSON use snake_case or camelCase? Are empty fields omitted or included as null? Are dates ISO 8601 strings or Unix timestamps? Are collections wrapped in a data envelope or returned as a bare array? None of these decisions are forced by JSON itself, and each one has consequences for the clients that consume the API.

Key point: Serialization converts objects to JSON; payload design decides what the JSON should look like. Consistent property naming, ISO 8601 dates, string enums, explicit null handling, and a predictable envelope structure are the decisions that determine whether clients can parse the API reliably across versions and implementations.


Why payload design matters more than serialization

The serializer’s defaults are not a design. A serializer like Jackson in Java or json_serializable in Dart will produce JSON from your objects, but the JSON it produces reflects the object’s field names and types, not the API’s contract. If the object has a field named createdAt and the database column is created_at, the serializer picks one and the client is stuck with it. The payload design decision is to choose the convention deliberately and apply it consistently, rather than letting the serializer’s default leak into the public API.

The client compatibility problem. A JSON API is a contract with every client that consumes it. Changing a field name from user_name to userName is a breaking change for every client that reads the old name. Changing an enum value from "active" to "ACTIVE" breaks every client that compares against the lowercase string. The payload design decisions that seem cosmetic at the start become compatibility constraints that are expensive to change later. The Microsoft API guidelines state that removing values from an enumeration list is a breaking change, and that enums should be treated as extensible so that new values can be added without breaking clients .

The parsing ambiguity problem. JSON has six value types: string, number, boolean, object, array, and null. It does not have a date type, a decimal type, or an enum type. Every date is a string, every decimal is a number (with the precision problems that follow), and every enum is a string or number that the client must interpret. The payload design decision is to choose a representation for each of these types that is unambiguous and widely supported. RFC 3339 for dates and ISO 8601 for durations are the standards that the Google JSON Style Guide recommends .

The null problem. A field that is null in the database can be omitted from the JSON, serialized as null, or serialized as an empty string. Each choice tells the client something different. Omission means “this field was not requested or is not applicable”; null means “this field exists but has no value”; an empty string means “this field exists and its value is empty.” The Google JSON Style Guide recommends removing empty or null values unless there is a strong semantic reason to keep them . The Microsoft guidelines use null to represent a field that is explicitly absent, and the client must distinguish between “field not present” and “field present but null” .


a. Property naming and structure conventions

The two dominant naming conventions in JSON APIs are camelCase and snake_case. The Google JSON Style Guide mandates camelCase for property names, with the first character being a letter, underscore, or dollar sign, and subsequent characters being letters, digits, underscores, or dollar signs . The Microsoft Azure guidelines use camelCase for property names as well. The choice is not about which is “better”โ€”both are widely usedโ€”but about consistency. A single API should not mix user_name and userName in the same payload.

The property names should be meaningful and singular unless the value is an array. The Google style guide states that array types should have plural property names, and all other property names should be singular . A field named users should be an array of user objects; a field named user should be a single user object. This convention makes the payload self-documenting: the client can tell whether a field is a collection or a single value from the name alone.

The structure should be flat where possible. Deeply nested objects are harder to parse, harder to validate, and harder to evolve. The Microsoft guidelines state that JSON objects may be used to group sub-fields, but that objects should be preferred over arrays whenever possible because arrays are more difficult to work with, especially in JSON Merge Patch where the entire array must be read before any operation is applied . A payload that nests five levels deep is a payload that will require five levels of null checks in every client.


b. Dates, enums, and the types JSON does not have

JSON has no date type. The representation is always a string, and the format is a design decision. The Google JSON Style Guide recommends RFC 3339 for dates, with the example "2007-11-06T16:34:41.000Z" . RFC 3339 is a profile of ISO 8601, and it is the format that JavaScript’s Date constructor parses natively, which makes it the practical choice for web APIs. The alternativeโ€”Unix timestamps as numbersโ€”is more compact but less readable and requires the client to know the timezone and precision. The RFC 3339 string is self-describing.

Enums are the other type JSON does not have. The Google style guide recommends representing enums as strings, because strings are more resilient to change: if the enum values are numbers, adding a new value can collide with an existing one, and if the values are strings, the client can gracefully handle unknown values by treating them as strings . The Microsoft guidelines go further and recommend “extensible enums”: the client should treat the enum field as a string that may contain values not documented in the current API version, so that the server can add new values without breaking clients . The client must not reject an unknown enum value; it should handle it gracefully.

The null decision is the third type problem. The Microsoft guidelines use null to mean “the field is present but has no value,” and they distinguish this from the field being absent. In a PATCH request, a missing field means “do not change this field,” while a field with a null value means “set this field to null” . The Google style guide takes the opposite approach for responses: remove empty or null values unless there is a strong reason to keep them . The two guidelines are not contradictoryโ€”they apply to different contexts. In a response, omission is cleaner; in a PATCH request, null is the explicit signal to clear a value.


c. The envelope and error payload

A JSON API can return a bare array for a collection, or it can wrap the array in an envelope. The bare array is simpler, but it leaves no place to put pagination metadata, links, or a total count. The envelopeโ€”typically a data field for the payload and optional meta, links, or errors fieldsโ€”is the standard approach in the specifications that have thought about this problem. The Google JSON Style Guide uses a top-level data object for the payload and a top-level error object for failures, but not both in the same response . The JSON:API specification uses a data member for the primary resource or collection, and an errors member for failures, with meta and links as optional top-level members .

The error payload is the part of the envelope that clients interact with most often. A bare {"error": "Not found"} is technically valid JSON, but it gives the client nothing to work with. The structured error payload includes a machine-readable code, a human-readable message, and optional details. The Microsoft guidelines show an error payload with a code, a message, and a details array for multiple errors, each with a target and a message . The RFC 7807 application/problem+json media type is the standard format for this: it defines type, title, status, detail, and instance fields that clients can parse without guessing at the shape of the error.

The pagination envelope is the other payload design decision that matters for collections. The Microsoft guidelines require the serialization payload to contain an opaque URL for the next page when multiple pages are available, using a field like @nextLink . The JSON:API specification uses links with next, prev, and self URLs. The bare array has no place for this metadata, which is why the envelope is the practical choice for any collection that can grow beyond a single page.


Complete Example Session

The following session designs a JSON payload for a user resource, showing the serialization decisions at each step: property naming, date format, enum representation, null handling, and the error envelope.

// ============================================
// PART 1: THE RESOURCE PAYLOAD
// ============================================
// The user resource with camelCase properties
// Dates are RFC 3339 strings
// The status is a string enum
// The optional fields are omitted when null
{
  "id": "usr_42",
  "userName": "alice",
  "email": "alice@example.com",
  "createdAt": "2026-10-18T12:00:00.000Z",
  "status": "active",
  "profile": {
    "displayName": "Alice",
    "avatarUrl": "https://example.com/avatar.png"
  }
}

// ============================================
// PART 2: THE COLLECTION ENVELOPE
// ============================================
// The collection is wrapped in a data field
// The meta field carries pagination information
// The links field carries the next page URL
{
  "data": [
    { "id": "usr_42", "userName": "alice", "status": "active" },
    { "id": "usr_43", "userName": "bob", "status": "inactive" }
  ],
  "meta": {
    "totalItems": 120,
    "pageIndex": 1,
    "itemsPerPage": 2
  },
  "links": {
    "self": "/v1/users?page=1",
    "next": "/v1/users?page=2"
  }
}

// ============================================
// PART 3: THE ERROR PAYLOAD
// ============================================
// The error uses the RFC 7807 problem+json shape
// The status matches the HTTP status code
// The errors array carries field-level details
{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "One or more fields are invalid.",
  "errors": [
    { "field": "email", "message": "Must be a valid email address" },
    { "field": "userName", "message": "Must be at least 3 characters" }
  ]
}

// ============================================
// PART 4: THE PATCH PAYLOAD
// ============================================
// A PATCH request uses JSON Merge Patch
// Only the fields to change are included
// A null value explicitly clears the field
{
  "email": "alice.new@example.com",
  "profile": null
}
// The userName and status are unchanged
// The profile is cleared

// ============================================
// PART 5: THE SERIALIZATION RULES
// ============================================
// camelCase: userName, createdAt, avatarUrl
// Dates: RFC 3339 strings
// Enums: string values (active, inactive)
// Nulls: omitted in responses, explicit in PATCH
// Envelope: data + meta + links for collections
// Errors: RFC 7807 problem+json

The five parts cover the resource payload, the collection envelope, the error payload, the PATCH payload, and the serialization rules. What the session demonstrates is that the payload is not just the object serializedโ€”it is a set of decisions about naming, types, envelopes, and null semantics that the client depends on.


Quick Reference

Naming Conventions

ConventionExampleSource
camelCaseuserNameGoogle, Microsoft
snake_caseuser_nameCommon in Python/Ruby APIs
Plural arraysusers, itemsGoogle style guide
Singular scalarsuser, totalGoogle style guide

Type Representations

TypeRecommended FormatExample
DateRFC 3339 string"2026-10-18T12:00:00Z"
DurationISO 8601 string"P3Y6M4DT12H30M5S"
EnumString"active"
NullOmit or explicitContext-dependent

Envelope Structures

StructureFieldsUse
Bare array[...]Simple, no pagination
Data envelopedata, meta, linksCollections with pagination
Error envelopetype, title, status, errorsRFC 7807

Null Semantics

ContextMeaning
Response, field omittedField not applicable or not requested
Response, field nullField exists but has no value
PATCH, field omittedDo not change the field
PATCH, field nullClear the field

Best Practices

โœ… Do This:

// Use camelCase consistently
{ "userName": "alice", "createdAt": "2026-10-18T12:00:00Z" }

// Use string enums
{ "status": "active" }

// Wrap collections with metadata
{ "data": [...], "meta": { "totalItems": 120 } }

// Use RFC 7807 for errors
{ "type": "...", "title": "...", "status": 422, "errors": [...] }

โŒ Don’t Do This:

// Mix naming conventions
{ "userName": "alice", "created_at": "..." }

// Use numbers for enums
{ "status": 1 } // what does 1 mean?

// Return bare arrays without pagination metadata
[ {...}, {...} ] // no total, no next page

// Return unstructured errors
{ "error": "Not found" }

Common Pitfalls

PitfallWhy It HappensFix
Breaking client on enum changeUsed numbers or removed valuesString enums, extensible
Timezone confusionUnix timestamp without timezoneRFC 3339 with Z
PATCH clears unintended fieldsTreated missing as nullOmit means “no change”
Pagination impossibleBare array responseEnvelope with next link
Error parsing failsUnstructured error stringRFC 7807 problem+json

Real-World Examples

1. camelCase resource

{ "userName": "alice", "createdAt": "2026-10-18T12:00:00Z" }

2. String enum

{ "status": "active" }

3. Collection envelope

{ "data": [...], "meta": { "totalItems": 120 }, "links": { "next": "/v1/users?page=2" } }

4. RFC 7807 error

{ "type": "https://api.example.com/errors/validation", "title": "Validation failed", "status": 422, "detail": "..." }

5. JSON Merge Patch

{ "email": "new@example.com", "profile": null }

6. Singular and plural

{ "user": {...}, "users": [...] }

7. ISO 8601 duration

{ "duration": "P3Y6M4DT12H30M5S" }

8. Pagination link

{ "@nextLink": "/v1/people?page=3" }

9. Field selection

{ "select": "id,userName,email" }

10. PATCH with omitted field

{ "email": "new@example.com" } // userName unchanged

Visual

Payload Design Decisions

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  NAMING                                      โ”‚
โ”‚  camelCase vs snake_case                     โ”‚
โ”‚  โ†’ choose one, apply everywhere              โ”‚
โ”‚                                              โ”‚
โ”‚  TYPES                                       โ”‚
โ”‚  dates โ†’ RFC 3339 strings                    โ”‚
โ”‚  enums โ†’ strings (extensible)                โ”‚
โ”‚  null โ†’ omit in responses                    โ”‚
โ”‚                                              โ”‚
โ”‚  ENVELOPE                                    โ”‚
โ”‚  collections โ†’ data + meta + links           โ”‚
โ”‚  errors โ†’ RFC 7807 problem+json              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Null Semantics

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  RESPONSE                                    โ”‚
โ”‚  field omitted โ†’ not applicable              โ”‚
โ”‚  field null โ†’ exists but empty               โ”‚
โ”‚                                              โ”‚
โ”‚  PATCH                                       โ”‚
โ”‚  field omitted โ†’ no change                   โ”‚
โ”‚  field null โ†’ clear the field                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Enum Evolution

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  VERSION 1                                   โ”‚
โ”‚  { "status": "active" }                      โ”‚
โ”‚                                              โ”‚
โ”‚  VERSION 2 adds "pending"                    โ”‚
โ”‚  { "status": "pending" }                     โ”‚
โ”‚                                              โ”‚
โ”‚  CLIENT must handle unknown values           โ”‚
โ”‚  as strings, not reject them                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Error Envelope

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  {                                           โ”‚
โ”‚    "type": "https://.../errors/validation",  โ”‚
โ”‚    "title": "Validation failed",             โ”‚
โ”‚    "status": 422,                            โ”‚
โ”‚    "detail": "One or more fields are invalid.",โ”‚
โ”‚    "errors": [                               โ”‚
โ”‚      { "field": "email", "message": "..." }  โ”‚
โ”‚    ]                                         โ”‚
โ”‚  }                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Default formatJSON
Naming conventioncamelCase (Google, Microsoft)
Date formatRFC 3339 string
Enum formatString, extensible
Null in responseOmit unless semantically necessary
Null in PATCHExplicit clear
Collection envelopedata + meta + links
Error formatRFC 7807 problem+json

Key takeaways:

  • Serialization is mechanical; payload design is architectural. The serializer converts objects to JSON, but the naming, types, envelopes, and null semantics are decisions that determine the API’s contract .
  • Property names should be consistent and meaningful. Use camelCase or snake_case throughout, use plural names for arrays and singular names for scalars, and avoid deeply nested structures where a flat object would do .
  • JSON has no date or enum type. Dates should be RFC 3339 strings, and enums should be strings that clients treat as extensible rather than as a fixed set .
  • Null means different things in responses and PATCH requests. In a response, omission is cleaner than null. In a PATCH request, omission means “do not change” and null means “clear” .
  • Collections need an envelope. A bare array has no place for pagination metadata or links. A data envelope with meta and links is the practical choice for any collection that can grow .
  • Errors need structure. A bare {"error": "Not found"} gives the client nothing to parse. RFC 7807 problem+json provides the fields (type, title, status, detail) that clients can rely on.

Remember: The JSON payload is the visible part of the API contract. The serializer’s defaults are not a design; they are a starting point that must be refined into a consistent set of decisions. The naming convention, the date format, the enum representation, the null semantics, the collection envelope, and the error structure are the decisions that determine whether clients can parse the API reliably and whether the API can evolve without breaking them. The specificationsโ€”Google’s style guide, Microsoft’s API guidelines, JSON:API, RFC 7807โ€”provide the conventions. The API’s job is to pick one set and apply it consistently.



Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!