What is JSON and How Does It Work?

A clear, practical explanation of what JSON is, how it works, and where it actually shows up in real projects, with examples and common mistakes..

If you have written any JavaScript at all, you have already used JSON, probably without thinking about it much. It is one of those things that gets explained as "a data format" in five seconds and then everyone moves on, but that short explanation skips over why it exists, why it looks the way it does, and where people actually trip over it in real projects. This post covers all of that, with examples you will actually run into.

What is JSON and How Does It Work?

What JSON Actually Is

JSON stands for JavaScript Object Notation. It is a text-based format for representing structured data, built around two things: key-value pairs and ordered lists. That is genuinely most of it. Here is a basic example:

{
  "name": "Sagor",
  "age": 28,
  "isActive": true,
  "skills": ["JavaScript", "Python", "SQL"],
  "address": {
    "city": "Dhaka",
    "country": "Bangladesh"
  }
}

Despite the name, JSON is not actually JavaScript. It started as a subset of JavaScript object syntax, which is why it looks so familiar if you write JS, but JSON is its own format with its own rules, and it is used by nearly every programming language now: Python, PHP, Java, Go, Rust, all of them can read and write JSON. The name stuck around for historical reasons more than technical accuracy.

Why JSON Won and XML Lost

Before JSON became the default, XML was the standard way to send structured data between systems, especially in web services. The same data in XML looks like this:

<person>
  <name>Sagor</name>
  <age>28</age>
  <skills>
    <skill>JavaScript</skill>
    <skill>Python</skill>
  </skills>
</person>

It works, but it is verbose, and every value needs an opening and closing tag. JSON does the same job with far less text, and more importantly, it maps directly onto data structures that programming languages already use internally: objects/dictionaries and arrays/lists. When a JavaScript app receives JSON, it does not need to parse a tree of XML nodes and walk through it. It just becomes a regular JavaScript object that you can immediately use with dot notation, like data.name or data.skills[0].

That direct mapping to native data structures, combined with being lightweight over the network, is the entire reason JSON took over. It was not a marketing decision, it solved a real performance and developer experience problem.

The Rules JSON Actually Follows

JSON looks loose and friendly, but it is stricter than people expect. A few rules that catch people off guard:

  • Keys must always be in double quotes. {name: "Sagor"} is valid JavaScript object syntax but invalid JSON. It has to be {"name": "Sagor"}.
  • No trailing commas. {"a": 1, "b": 2,} will fail to parse because of that last comma before the closing brace. JavaScript object literals tolerate this, JSON does not.
  • No comments. There is no syntax for comments in JSON at all, not //, not /* */. If you need a config file with comments, JSON is the wrong format (use something like YAML or JSON5 instead).
  • Only specific data types are allowed: strings, numbers, booleans, null, objects, and arrays. No dates, no functions, no undefined. If you have a JavaScript Date object and JSON-stringify it, it gets converted into a plain string, and you are responsible for converting it back into a real date when you read it again.
  • Strings must use double quotes, not single quotes. {'name': 'Sagor'} is invalid JSON even though it is valid JS.

These rules exist because JSON is meant to be parsed by every language consistently, not just JavaScript. The stricter the grammar, the less ambiguity there is across different parsers.

Turning JSON Into Usable Data (And Back Again)

In JavaScript, there are exactly two functions you need for almost everything involving JSON.

Parsing a JSON string into a real object:

const jsonString = '{"name": "Sagor", "age": 28}';
const obj = JSON.parse(jsonString);

console.log(obj.name); // "Sagor"
console.log(obj.age);  // 28

Converting an object into a JSON string:

const user = { name: "Sagor", age: 28, isActive: true };
const jsonString = JSON.stringify(user);

console.log(jsonString); // '{"name":"Sagor","age":28,"isActive":true}'

This pair is everywhere. When your frontend sends data to a server, it stringifies a JavaScript object into JSON text to put it in the request body. When the server sends data back, your code parses that JSON text back into an object you can work with. Fetch actually does the parsing step for you if you call response.json(), which is just a shortcut for reading the response body as text and running JSON.parse() on it.

Common Mistakes People Actually Make

Trying to JSON.parse something that is already an object. This throws an error because JSON.parse expects a string, not an object:

const data = { name: "Sagor" };
JSON.parse(data); // Error: Unexpected token o in JSON

// If data is already an object, you don't need to parse it at all
console.log(data.name); // just use it directly

This usually happens when someone calls response.json() from Fetch (which already returns a parsed object) and then tries to JSON.parse() it again somewhere downstream.

Forgetting that stringify drops things silently. Functions, undefined values, and Symbols all disappear when you stringify an object, with no warning:

const obj = {
  name: "Sagor",
  greet: function() { return "hi"; },
  middleName: undefined
};

console.log(JSON.stringify(obj));
// {"name":"Sagor"}
// greet and middleName are both gone

If your API call is silently missing a field, check whether the value was undefined before you stringified it. This is a very common source of "why is this field missing on the server" bugs.

Sending a JavaScript object directly as a fetch body instead of stringifying it first.

// Wrong, this sends "[object Object]" as a string
fetch(url, {
  method: 'POST',
  body: { name: "Sagor" }
});

// Correct
fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: "Sagor" })
});

Assuming numbers stay precise. JSON numbers follow JavaScript's standard floating point rules, so very large integers (bigger than Number.MAX_SAFE_INTEGER, around 9 quadrillion) can lose precision when parsed. This trips people up with things like large database IDs or financial values that come from a backend using 64-bit integers. If exact precision matters, the API should send that value as a string, not a raw number.

Where JSON Actually Shows Up in Real Projects

  • API responses and requests. Nearly every REST API today sends and receives JSON, including this exact scenario from connecting to third-party APIs with Fetch.
  • Config files. package.json in every Node project, tsconfig.json, composer.json in PHP, and many others use JSON as the config format.
  • Browser storage. localStorage only stores strings, so saving an object means running JSON.stringify() before saving it and JSON.parse() when reading it back.
  • Databases. Many databases, including PostgreSQL and MongoDB, support storing and querying JSON directly inside columns or documents, instead of forcing every field into a rigid table structure.
  • Message queues and webhooks. When one service notifies another about an event (a payment completing, a new signup, a file upload finishing), the payload is almost always JSON.

A Quick Example Combining All of It

Here is a small realistic flow: saving a user's settings to localStorage, then loading them back later.

// Saving settings
function saveSettings(settings) {
  localStorage.setItem('userSettings', JSON.stringify(settings));
}

saveSettings({ theme: 'dark', language: 'bn', notifications: true });

// Loading settings, with a fallback if nothing is saved yet
function loadSettings() {
  const saved = localStorage.getItem('userSettings');

  if (!saved) {
    return { theme: 'light', language: 'en', notifications: false };
  }

  try {
    return JSON.parse(saved);
  } catch (error) {
    console.error('Saved settings were corrupted, using defaults:', error);
    return { theme: 'light', language: 'en', notifications: false };
  }
}

Notice the try/catch around JSON.parse. If the stored string is somehow malformed (manually edited, corrupted, or from an older version of your app with a different shape), parsing throws an error, and your app should not crash because of it. This pattern, parse inside a try block with a sane fallback, is worth using anywhere you are reading JSON from a source you do not fully control, including local storage, files, or third-party APIs.

Wrapping Up

JSON is simple by design, and that simplicity is exactly why it took over as the standard format for moving data between systems. The actual skill is not memorizing the syntax, it is knowing the handful of rules that differ from regular JavaScript objects, understanding what gets silently dropped during conversion, and handling parse errors defensively when the data is not fully under your control. Once those habits are in place, JSON stops being something you think about and just becomes the plumbing that quietly works in the background of everything you build.

Post a Comment