What Is JSON and How to Format It Correctly

A complete guide for developers — from JSON basics to formatting, validation, and debugging

JSONMay 1, 20268 min readBy Keyur Patel

If you've ever opened the Network tab in DevTools and squinted at a wall of unformatted text, you've met JSON. It's everywhere — API responses, config files, package.json, database exports. And yet, for something so fundamental, it trips people up constantly. Trailing commas, missing quotes, that one unescaped backslash that crashes your parser at 2 AM.

I wrote this guide because I got tired of explaining the same JSON gotchas to teammates. Whether you're just starting out or you've been writing code for years and still occasionally forget that JSON doesn't allow comments — this covers everything from basics to the weird edge cases that cause production bugs.

So What Actually Is JSON?

JSON stands for JavaScript Object Notation. Don't let the name fool you — it's not just for JavaScript. Every language you can think of (Python, Java, Go, Rust, PHP, Ruby, C#) has built-in JSON support. It's the lingua franca of web APIs.

At its core, JSON is just text that follows specific formatting rules. It looks like a JavaScript object, but stricter. Think of it as a universal envelope for sending structured data between any two systems that speak different languages.

JSON supports six data types — that's it. No dates, no functions, no undefined. Just these:

  • String — text enclosed in double quotes, e.g. "hello"
  • Number — integer or floating-point, e.g. 42 or 3.14
  • Booleantrue or false
  • Null — represents an empty or absent value: null
  • Object — a collection of key-value pairs enclosed in curly braces {}
  • Array — an ordered list of values enclosed in square brackets []

JSON Syntax Rules (Where People Mess Up)

Here's the thing about JSON — it's strict. Way stricter than JavaScript objects. If you've ever pasted something into an API and got "Unexpected token" back, one of these rules was violated:

  • Keys must be double-quoted. Not single quotes. Not unquoted. Double quotes only.
  • Strings must use double quotes. 'hello' is invalid. "hello" is correct.
  • No trailing commas. That comma after the last item? JSON hates it. JavaScript doesn't care, JSON does.
  • No comments. Yeah, this one hurts. You can't add // todo: fix this in JSON.
  • No leading zeros on numbers. 007 isn't valid. 7 is.
  • Special characters must be escaped. Newlines become \n, tabs become \t.

Valid JSON Example

{
  "name": "Alice",
  "age": 30,
  "isAdmin": true,
  "address": {
    "city": "London",
    "postcode": "EC1A 1BB"
  },
  "tags": ["developer", "designer"]
}

Invalid JSON — Common Mistakes

{
  name: "Alice",          // ❌ Key not quoted
  'age': 30,              // ❌ Single-quoted key
  "isAdmin": true,        // ❌ Trailing comma
  // This is a comment    // ❌ Comments not allowed
}

Why Bother Formatting JSON?

APIs send minified JSON because smaller = faster transfer. But try reading this:

{"user":{"id":1,"name":"Alice","roles":["admin","editor"],"settings":{"theme":"dark","notifications":true}}}

Now look at the same thing, properly indented:

{
  "user": {
    "id": 1,
    "name": "Alice",
    "roles": ["admin", "editor"],
    "settings": {
      "theme": "dark",
      "notifications": true
    }
  }
}

Night and day difference. You can immediately spot structure, find the field you need, and notice if something's missing. When you're debugging a failed API call at midnight, formatted JSON is the difference between finding the bug in 10 seconds vs 10 minutes.

The Errors That'll Drive You Crazy (And How to Fix Them)

Unexpected token

Cause: Usually caused by a missing comma, extra comma, or unquoted key.

Fix: Check the line number in the error message. Look for missing or extra commas and ensure all keys are double-quoted.

Unexpected end of JSON input

Cause: The JSON is incomplete — a bracket or brace was opened but never closed.

Fix: Count your opening and closing brackets. Every { needs a }, and every [ needs a ].

Invalid escape character

Cause: A backslash in a string is not followed by a valid escape sequence.

Fix: Escape backslashes as \\ and ensure special characters like newlines use \n.

Trailing comma

Cause: A comma after the last item in an object or array.

Fix: Remove the comma after the last key-value pair or array element.

JSON in Real-World APIs and Applications

JSON is the de facto standard for data exchange in modern web APIs. Whether you are integrating with a payment processor, a social media platform, or a cloud service, the response you receive will almost certainly be JSON. Understanding how major APIs structure their JSON responses helps you work with them more efficiently.

The GitHub API, for example, returns deeply nested JSON objects when you fetch repository data. A single request to GET /repos/owner/repo returns an object with dozens of fields including nested objects for the owner, license, and organization. The Stripe API uses JSON for every payment object, with nested metadata, line items, and customer information. Twitter's (now X) API returns tweet objects with nested user objects, media arrays, and engagement metrics — all as JSON.

REST vs GraphQL JSON Responses

REST APIs return fixed JSON structures defined by the server. You get what the endpoint gives you, which sometimes means over-fetching (receiving more data than you need) or under-fetching (needing multiple requests to get all the data you want).

GraphQL solves this by letting clients specify exactly which fields they want. The response is still JSON, but the shape mirrors your query exactly. This means smaller payloads and fewer round trips. Both approaches use JSON — the difference is in how the structure is negotiated between client and server.

GraphQL JSON Response Example

{
  "data": {
    "user": {
      "name": "Alice",
      "email": "alice@example.com",
      "posts": [
        { "title": "Hello World", "publishedAt": "2024-01-01" }
      ]
    }
  }
}

JSON Schema Validation

JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It lets you define the expected structure of a JSON object — which fields are required, what types they should be, and what constraints they must satisfy. This is invaluable for API contract testing and ensuring data integrity.

JSON Schema Example

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["name", "age"],
  "properties": {
    "name": { "type": "string", "minLength": 1 },
    "age":  { "type": "integer", "minimum": 0, "maximum": 150 },
    "email": { "type": "string", "format": "email" }
  }
}

Libraries like ajv (JavaScript), jsonschema (Python), and javax.validation (Java) can validate JSON against a schema at runtime, catching malformed data before it causes bugs deeper in your application.

Testing JSON APIs with Postman

Postman is the most widely used tool for testing and exploring JSON APIs. It lets you send HTTP requests, inspect JSON responses with syntax highlighting, and write automated test scripts that validate response structure. You can also use Postman to generate code snippets in JavaScript, Python, curl, and other languages for any request you build.

For command-line workflows, curl combined with jq is a powerful alternative. The jq tool is a lightweight JSON processor that lets you filter, transform, and format JSON directly in the terminal.

Handling Nested JSON in JavaScript

Working with deeply nested JSON in JavaScript requires care to avoid runtime errors when a property does not exist. The optional chaining operator (?.) introduced in ES2020 makes this much safer:

const data = {
  user: {
    address: {
      city: "London"
    }
  }
}

// Without optional chaining — throws if address is missing
const city = data.user.address.city

// With optional chaining — returns undefined safely
const city = data.user?.address?.city

// With nullish coalescing — provide a default
const city = data.user?.address?.city ?? "Unknown"

// Destructuring nested JSON
const { user: { address: { city = "Unknown" } = {} } = {} } = data

When working with arrays of JSON objects, Array.map(), Array.filter(), and Array.reduce() are your primary tools for transforming and extracting data. For large JSON datasets, consider using streaming parsers like JSONStream in Node.js to avoid loading the entire document into memory at once.

Tools for Working with JSON

You do not need to format JSON manually. Use these free tools to format, validate, and repair JSON instantly in your browser:

JSON in APIs and Data Exchange

JSON has become the universal language of web APIs. Understanding how JSON is used in API communication — from Content-Type headers to pagination patterns to streaming large responses — is essential knowledge for any developer working with modern web services.

JSON as the Standard for REST APIs

Virtually every modern REST API uses JSON as its data interchange format. When you call the GitHub API, Stripe API, Twitter API, or any major web service, you send and receive JSON. This was not always the case — XML dominated API communication until around 2012, when JSON's simplicity, smaller payload size, and native JavaScript parsing made it the clear winner for web applications.

JSON's dominance comes from several practical advantages: it maps directly to data structures in most programming languages (objects/dictionaries, arrays, primitives), it is human-readable for debugging, it has minimal syntax overhead compared to XML, and every language has fast, battle-tested JSON parsers available.

Content-Type Headers

When working with JSON APIs, the correct HTTP headers are essential. The Content-Type header tells the server what format the request body is in, and the Accept header tells the server what format you want the response in. Getting these wrong is a common source of "400 Bad Request" errors.

// Sending JSON in a request
fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",   // Tell server: body is JSON
    "Accept": "application/json"          // Tell server: send JSON back
  },
  body: JSON.stringify({
    name: "Alice",
    email: "alice@example.com"
  })
})

// Common Content-Type values for JSON:
// application/json          — standard JSON
// application/problem+json  — RFC 7807 error responses
// application/vnd.api+json  — JSON:API specification
// application/ld+json       — JSON-LD (linked data)

// ❌ Common mistake: forgetting Content-Type
// Server receives raw text, cannot parse as JSON → 400 error

JSON vs XML Comparison

While JSON has largely replaced XML for web APIs, understanding the trade-offs helps you make informed decisions when working with legacy systems or specific domains (like SOAP services, RSS feeds, or SVG graphics) that still use XML.

FeatureJSONXML
ReadabilityConcise, minimal syntaxVerbose with open/close tags
Parsing speedVery fast (native in JS)Slower (requires XML parser)
Data typesString, number, boolean, null, object, arrayEverything is a string (types via schema)
CommentsNot supportedSupported (<!-- -->)
Schema validationJSON SchemaXSD (more mature, more complex)
NamespacesNot supportedFull namespace support
Payload sizeSmaller (30-50% less)Larger due to tags
Browser supportJSON.parse() built-inDOMParser required

JSON Schema for Validation

JSON Schema lets you define the expected structure of a JSON document and validate data against it at runtime. This is invaluable for API contract testing, form validation, configuration file validation, and ensuring data integrity between services. Think of it as TypeScript types for JSON data — but enforced at runtime.

// Define a schema for a user object
const userSchema = {
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["name", "email", "age"],
  "properties": {
    "name":  { "type": "string", "minLength": 1, "maxLength": 100 },
    "email": { "type": "string", "format": "email" },
    "age":   { "type": "integer", "minimum": 0, "maximum": 150 },
    "roles": {
      "type": "array",
      "items": { "type": "string", "enum": ["admin", "editor", "viewer"] },
      "uniqueItems": true
    }
  },
  "additionalProperties": false
}

// Validate with ajv (fastest JSON Schema validator for JS)
const Ajv = require("ajv")
const ajv = new Ajv()
const validate = ajv.compile(userSchema)

const isValid = validate({ name: "Alice", email: "alice@example.com", age: 30 })
// → true

const isInvalid = validate({ name: "", email: "not-an-email", age: -1 })
// → false
// validate.errors → [{ message: "must NOT have fewer than 1 characters" }, ...]

Common API Response Patterns

Most well-designed APIs follow consistent response patterns. Understanding these patterns helps you consume APIs more efficiently and design your own APIs in a way that clients expect.

// Envelope pattern — wraps data with metadata
{
  "success": true,
  "data": { "id": 1, "name": "Alice" },
  "meta": { "requestId": "req_abc123", "timestamp": 1714521600 }
}

// Pagination pattern — cursor-based (preferred)
{
  "data": [{ "id": 1 }, { "id": 2 }, { "id": 3 }],
  "pagination": {
    "nextCursor": "eyJpZCI6M30=",
    "hasMore": true,
    "totalCount": 150
  }
}

// Pagination pattern — offset-based (simpler but less performant)
{
  "data": [...],
  "pagination": {
    "page": 2,
    "perPage": 20,
    "totalPages": 8,
    "totalCount": 150
  }
}

// Error response pattern (RFC 7807 Problem Details)
{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation Error",
  "status": 422,
  "detail": "The email field is not a valid email address",
  "errors": [
    { "field": "email", "message": "must be a valid email address" }
  ]
}

Handling Large JSON Responses: Streaming with JSON Lines

Standard JSON requires the entire document to be parsed at once — the parser needs to see the closing bracket before it can return any data. For large datasets (millions of records, real-time event streams), this is impractical. JSON Lines (also called NDJSON — Newline Delimited JSON) solves this by putting one complete JSON object per line, allowing line-by-line streaming and processing.

// JSON Lines format (each line is a valid JSON object)
{"id": 1, "event": "page_view", "timestamp": 1714521600}
{"id": 2, "event": "click", "timestamp": 1714521601}
{"id": 3, "event": "purchase", "timestamp": 1714521602}

// Processing JSON Lines in Node.js (streaming)
const readline = require("readline")
const fs = require("fs")

const rl = readline.createInterface({
  input: fs.createReadStream("events.jsonl")
})

rl.on("line", (line) => {
  const event = JSON.parse(line) // Parse one object at a time
  processEvent(event)            // Process immediately, no buffering
})

// Streaming from an API using fetch
const response = await fetch("https://api.example.com/events/stream")
const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ""

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  buffer += decoder.decode(value)
  const lines = buffer.split("\n")
  buffer = lines.pop() // Keep incomplete line in buffer
  for (const line of lines) {
    if (line.trim()) processEvent(JSON.parse(line))
  }
}

JSON Lines is used by logging systems (structured logs), data pipelines (BigQuery, Elasticsearch bulk API), AI/ML training data, and real-time event streams. The Content-Type for JSON Lines is application/x-ndjson or application/jsonl.

Wrapping Up

Look, JSON isn't glamorous. Nobody gets excited about curly braces. But it's the glue holding modern software together, and knowing its quirks saves you hours of debugging. Remember: double quotes only, no trailing commas, no comments, validate before you ship.

If you're working with JSON daily (and let's be honest, you probably are), keep a formatter handy. Your future self will thank you when that 3000-line API response needs debugging.