1. In HTTP, body needs a data format
If you look at HTTP realistically, then body is just a chunk of data. Not “JSON by default”, not “a string”, not “an object”, but a set of bytes. You can put anything into that set: an image, an HTML page, a PDF, a binary archive, the text “OK”, and even a poem of 3,000 lines. But an API is not a poetry club; it needs a format that unambiguously describes the data structure.
In a typical backend API, we almost always want to send structured data: “here is a book, it has title and author”, “here is a list of items, and their count”, “here is an error, and it has errorCode and message”. If you send this as arbitrary text, you will have to agree on rules manually (“the first word is title, the second is author, and if there is a space in the name, then that is it, good luck”). Such agreements break at the first change. That is why the industry fairly quickly came to the idea: let us use a format where the data structure is expressed explicitly instead of being guessed “by word position”.
JSON became the standard answer to that need. It is simple enough to read with your eyes (even when you are tired and want to go home), and strict enough to be parsed programmatically without imagination. Important: JSON “lives” inside the HTTP contract, not instead of it, and its existence must always be confirmed by headers.
A small Java snippet, just to pin down the idea that the body format is part of the agreement:
String contentType = "application/json";
// Text block is more readable for JSON than escaping quotes in a single line
String responseBody = """
{ "status": "UP", "appName": "readlater-api" }
""";
// Content-Type says: "this is JSON, parse it according to JSON rules"
System.out.println(contentType); // application/json
System.out.println(responseBody.strip()); // { "status": "UP", "appName": "readlater-api" }
Pay attention: both values matter here. responseBody without Content-Type is just a string. Content-Type without responseBody is a promise, but not the result.
2. JSON: “structure in text”
There are different data formats in the world, but JSON won popularity in APIs for a rather prosaic reason: it is convenient for almost everyone involved. It is convenient for the client because JSON is easy to parse in practically any language. It is convenient for the server because JSON is easy to generate. It is convenient for the developer because JSON can be opened in Postman, seen with the eyes, and quickly understood: “what is even happening?”
The key feature of JSON is that it is textual, but at the same time structured. It is like a form: on one hand, just a sheet of paper with text; on the other, it has fields, and their meaning is clear. And this combination of “text + structure” makes JSON an excellent candidate for data exchange over HTTP.
When the data format is textual, you get a debugging superpower. Any problem turns from “something broke somewhere” into “I see a specific body, I see specific fields, and I can compare them with the contract”. That is why, at the learning stage and in the first backend steps, JSON is ideal: it helps keep the connection between the abstraction of a “contract” and the actual bytes on the network.
Another reason for the popularity of JSON is that it fits the typical data shapes of backend APIs well. For example, “one object”, “a list of objects”, “an error with details” — all of this is naturally expressed with JSON structures. Yes, in practice there are nuances, but the basic set covers 80% of the life of an ordinary API.
To feel the difference between “arbitrary text” and “structure”, compare two strings. Both are “readable”, but only one has a strict form:
// Arbitrary text: the format rules need to be "inferred" and maintained by hand
String plainText = "title=Clean Code; author=Robert C. Martin";
// JSON: structure is fixed (fields + values), so it can be parsed reliably
String jsonText = """
{ "title": "Clean Code", "author": "Robert C. Martin" }
""";
System.out.println(plainText); // title=Clean Code; author=Robert C. Martin
System.out.println(jsonText.strip()); // { "title": "Clean Code", "author": "Robert C. Martin" }
In the first variant, we “hope” that nobody will insert ; into the title, and that the order will always be the same. In the second variant, the structure is fixed explicitly: there is an object, it has named fields, and each field has a value.
3. JSON in the HTTP contract: headers and status
A beginner’s temptation is to think that “the contract = JSON”. In reality, the contract is broader: it includes the method, the path, the status code, the headers, and the shape of the body. JSON is just one part of this structure. And that is exactly why Content-Type and (on the client side) Accept are so important in APIs: they say how to understand the message body.
Imagine that you were sent the string {"status":"UP"}. Is that JSON? Possibly. Is it just text? Also possible. Is it part of some other structure? Theoretically, yes. To avoid guessing, HTTP has an explicit format marker — the Content-Type header. It makes the exchange more mature: not “guess the melody from one byte”, but “handle the declared format”.
In practice, it looks something like this: the client sends a request and says “I want JSON”, the server responds and confirms “yes, I returned JSON”. In a short diagram:
sequenceDiagram
participant C as "Client (Postman/application)"
participant S as "Server (API)"
C->>S: "Request Accept: application/json"
S->>C: "Response 200 OK Content-Type: application/json { ...JSON... }"
One more important point here: the status code and JSON are different levels of meaning. If the JSON contains a field status: "UP", that is not the HTTP status. The HTTP status is 200, 404, 500, etc. The status field is just data that you put into the body. It is like two different “statuses” in life: “I am in a relationship” and “I replied 200 OK” — do not mix them up.
We will see this especially clearly in the ReadLater Starter project when we check the API through Postman: the same JSON can come with different HTTP status codes (for example, a validation error and a successful response are different statuses), and that is normal. So from now on, we get used to this: the response body is data, the status is the result of processing at the protocol level, and the headers are metadata and a format agreement.
4. JSON limitations: “no-surprises mode”
There is a paradoxical idea: JSON is good not because it can do everything, but because it cannot do too much. It has a small set of types, simple rules, and a fairly strict syntax. And this “humility” turns into predictability.
If JSON allowed a million different constructs, like programming languages do, then any API would be a lottery: “how did they encode the date today?”, “what does this construct mean?”. But JSON is limited. It allows you to describe structure with objects and arrays and store values as strings/numbers/booleans/null. That means the client and server usually agree on the rules once — and then simply follow them.
There is another bonus for a backend developer: JSON is well suited for “contract thinking”. You see fields, you see values, you see nesting — and you can say out loud what each part means. This matters in a team: when you discuss an API, you need to say not “some weird thing comes back there”, but “the response has a top-level object, inside it an items array, and each element has id and status”.
For our future ReadLater Starter this is critical, because the project will live in two worlds: first we will receive JSON from an external book catalog, and then we will start returning JSON from our local API. If the data format is “fuzzy”, you will not be able to write code that relies on it reliably. If the format is fixed and limited, it becomes a dependable foundation.
That is why today we start with the simple thing: understand why JSON is convenient and predictable, and which basic rules make it a “honest format” rather than “a string that almost looks like JSON”.
5. Strict JSON syntax
At the beginning, it seems that JSON is something like “well, I will put braces and colons and everything will be fine”. But JSON really does not like approximation. It is not one of those formats that “will figure it out”. If JSON is invalid, the parser is not required to “guess” what you meant. It simply says: “No, that is not allowed.” And in a sense, that is good: fewer surprises, more discipline.
The most common basic rules that break beginners’ lives (and make them stare at the screen like it is the Matrix) are not complicated: strings and field names must be in double quotes, you cannot leave a trailing comma after the last element, and the structure itself must be properly closed with braces. Yes, this is that rare case where “one extra comma” can really ruin the evening.
Look at a pair of lines. They seem similar, but one of them is not JSON:
// Valid JSON: double quotes and no "trailing" commas
String validJson = "{ \"title\": \"Clean Code\" }";
// Invalid JSON: single quotes + an extra comma at the end
String invalidJson = "{ 'title': 'Clean Code', }";
System.out.println(validJson); // { "title": "Clean Code" }
System.out.println(invalidJson); // { 'title': 'Clean Code', }
The second variant is a typical “almost JSON”, which often appears after copying from pseudo-examples or from a JavaScript object (yes, a JavaScript object and JSON are not the same thing, but without going into depth). For APIs, this matters: the client can be written in any language, and it expects valid JSON, not “well, it sort of looks similar”.
Another small nuance: JSON is valid regardless of how “pretty” it is. You can make perfectly formatted but invalid JSON — and it will not work. And you can make ugly, compact JSON — and it will work every time. Beauty is for humans, validity is for machines. And, spoiler, machines are usually more important to you.
6. Spaces and line breaks in JSON
When you start working with JSON, two modes appear very quickly: “compact” and “readable”. Compact is what you encounter in real service responses (they save traffic, and Postman will show you the “pretty” version anyway). Readable is what you make yourself in documentation, examples, and during debugging. And here is the important idea: both variants describe the same data, as long as the formatting changes between JSON tokens, not inside string values.
Put simply: spaces and line breaks between JSON tokens are JSON makeup. They make it more attractive, but they do not change its identity. The data stays the same. A space inside a string, however, is already part of the value.
Compare two representations of the same object:
// Compact form: minimum symbols, same meaning
String compact = "{\"title\":\"Clean Code\",\"author\":\"Robert C. Martin\"}";
// Readable form: spaces added for humans, the parser does not care about them
String readable = "{ \"title\": \"Clean Code\", \"author\": \"Robert C. Martin\" }";
System.out.println(compact); // {"title":"Clean Code","author":"Robert C. Martin"}
System.out.println(readable); // { "title": "Clean Code", "author": "Robert C. Martin" }
Both strings are valid. In a real API, you will more often get something closer to compact, and in tools (Postman, IDE) you will see a nicely formatted version. It is especially useful to remember this when you “compare responses by eye”: differences in spaces do not mean differences in meaning.
And yes, this is where a useful habit is born: when you look at JSON, you are not looking for “beauty”, but for structure. What is at the top level: an object or an array? Which fields? Which values? And only after that — indentation, alignment, line breaks. Postman and other tools will help you with beauty, and you need to learn to see the meaning.
To connect this with our project, imagine a future /health response (we will implement it much later, but the JSON shape is typical). It can be “on one line” or nicely formatted — the contract does not change:
// In "production" responses, you often get something closer to a single line
String healthCompact = "{\"status\":\"UP\",\"appName\":\"readlater-api\"}";
// And in examples/documentation, it is more convenient to keep a readable form
String healthPretty = """
{ "status": "UP", "appName": "readlater-api" }
""";
System.out.println(healthCompact); // {"status":"UP","appName":"readlater-api"}
System.out.println(healthPretty.strip()); // { "status": "UP", "appName": "readlater-api" }
The only important thing is this: both versions must be valid JSON and match the agreed structure.
7. Typical mistakes when working with JSON
Mistake #1: treating JSON as “any text in curly braces”.
This mistake usually looks like this: a person sees { ... } and automatically decides that it is JSON, which means “no need to worry”. In practice, “curly braces” are not yet a format. JSON requires strict quotes, commas, colons, and correct structure closure. Good prevention is simple: if you write JSON by hand yourself (in an example, in documentation, in Postman), treat it like code — one extra comma really does break everything.
Mistake #2: forgetting that the body format is confirmed by the Content-Type header.
It is very easy to get stuck on “what matters is what is inside the body” and treat headers as secondary. But for the HTTP contract, headers are part of the agreement. If the server returns JSON, it must honestly write Content-Type: application/json. If the client sends JSON, it must set Content-Type in the request. Otherwise, the other side will either parse the body incorrectly or pretend not to understand anything at all (and, strictly speaking, it will be right).
Mistake #3: confusing JSON validity with its “prettiness”.
Beginners often think that if JSON is nicely formatted, then it must be correct. But formatting is just cosmetics. Invalid JSON can be perfectly aligned with indentation and still fail to parse. Valid JSON can look like “a mess in one line” and still be perfect for the machine. So the rule is: first check validity and structure, then think about readability.
Mistake #4: using single quotes or leaving a trailing comma “out of habit”.
This is a classic story, especially if a person previously wrote JavaScript or just got used to “doing it that way”. In JSON, field names and strings are in double quotes, and a comma after the last element is not allowed. The JSON parser is not required to “understand what you meant”; it works by the rules. Here, only discipline and attention help — like when typing a password, only without the right to “show characters”.
Mistake #5: treating JSON as “just text” and trying to pull values out by hand with substring.
Even if it seems that “it is just "title":"Clean Code" — I will find it as a string”, that quickly turns into a trap. Any formatting change, field order, or nesting — and your “regex” starts living its own life. At the level of this lecture, it is enough to fix one idea: JSON is structured data. If you need to extract values, then in the future you will parse it as a structure, not as a random set of symbols.
GO TO FULL VERSION