hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses HTTP from scratch

What is HTTP

  • The request-response model
  • Anatomy of an HTTP request
  • Anatomy of an HTTP response

Methods

  • GET and HEAD
  • POST
  • PUT, PATCH, and DELETE
  • OPTIONS and CORS preflight

Status codes

  • 2xx success
  • 3xx redirection
  • 4xx client errors
  • 5xx server errors

Headers

  • Request headers
  • Response headers
  • Custom headers

The body

  • JSON
  • Form data and multipart
  • No body

Connections

  • TCP, DNS, and TLS
  • HTTP/1.1 vs HTTP/2
  • Cookies and state

Putting it all together

  • Building a server from scratch
  • From scratch to framework

JSON

The language APIs speak

We have been seeing JSON in every lesson so far. Every time a client sends data or a server returns data, it has been in JSON format. Now it is time to actually understand what JSON is and how it works, because it is the default data format for virtually every web API you will build or interact with.

JSON stands for JavaScript Object Notation. It is human-readable, compact, and natively supported by JavaScript (which is convenient, since that is what we are writing). But despite the name, JSON is language-independent. Every programming language has tools to read and write it.

The structure

JSON has six types. That is it. Six.

{
  "id": "book-1",
  "title": "Kindred",
  "rating": 4.5,
  "published": true,
  "sequel": null,
  "genres": ["science-fiction", "historical-fiction"],
  "author": {
    "id": "author-3",
    "name": "Octavia Butler"
  }
}

Let’s walk through each one:

Strings are text wrapped in double quotes: "hello". Single quotes are not valid JSON. This is a common source of parsing errors.

Numbers are unquoted: 42, 3.14, -1. No trailing commas, no leading zeros.

Booleans are lowercase: true, false. Not True, not TRUE.

null represents “nothing”: null. Not undefined (that is a JavaScript thing, not a JSON thing).

Arrays are ordered lists: [1, 2, 3], ["a", "b"], [{"id": 1}, {"id": 2}]. They can hold any mix of types.

Objects are collections of key-value pairs: {"key": "value"}. Keys must be strings (double-quoted).

That is the entire language. If you understand these six types, you understand JSON.

Sending JSON from the client

const response = await fetch("https://api.example.com/books", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Kindred", genre: "science-fiction" }),
});

Two things are happening here. JSON.stringify takes a JavaScript object and converts it to a JSON string. The Content-Type: application/json header tells the server “the body I am sending is JSON, so parse it accordingly.”

What would happen if you skipped JSON.stringify and passed the object directly? The body would become [object Object], which is not valid JSON. The body option in fetch expects a string (or a few other specific types), not a plain object. JSON.stringify is the bridge between a JavaScript object and a JSON string.

Receiving JSON from the server

const response = await fetch("https://api.example.com/books/book-1");
const book = await response.json();
console.log(book.title); // "Kindred"

response.json() does the reverse of JSON.stringify. It reads the response body as text and parses it into a JavaScript object. After this call, book is a regular JavaScript object that you can access with dot notation.

JSON on the server side

When you are building the server, Response.json() handles everything:

route.get("/books/:id", {
  resolve: (c) => {
    const id = new URL(c.request.url).pathname.split("/").pop();
    const book = db.prepare("SELECT * FROM books WHERE id = ?").get(id);
    return Response.json(book); // Sets Content-Type and serializes automatically
  },
});

Response.json(book) does three things at once: it calls JSON.stringify on book, sets the Content-Type header to application/json, and creates a response with status 200. You do not need to do any of that manually.

When JSON parsing fails

What happens if someone sends you invalid JSON? JSON.parse (which response.json() uses internally) throws a SyntaxError:

JSON.parse("not json"); // SyntaxError: Unexpected token 'n'
JSON.parse("{'bad': 'quotes'}"); // SyntaxError: single quotes are invalid
JSON.parse("{trailing: comma,}"); // SyntaxError: trailing comma

[!NOTE] The Error Handling course’s JavaScript Error Types lesson covered SyntaxError as one of the built-in error types. JSON.parse is the most common place you will see SyntaxError at runtime.

JSON is the format you will use in almost every API. But it is not the only body format. HTML forms use a different encoding, and file uploads use yet another. The next lesson covers form data and multipart.

Exercises

Exercise 1: Create a JSON object that uses every type: string, number, boolean, null, array, and a nested object. Run it through JSON.stringify and then JSON.parse. Verify it round-trips correctly.

Exercise 2: Try to parse invalid JSON strings with JSON.parse. Catch the SyntaxError and log the error message.

Exercise 3: Send a POST request with a JSON body. Read it on the server. Verify the data matches what you sent.

Why must API clients set Content-Type: application/json when sending JSON?

← Custom headers Form data and multipart →

© 2026 hectoday. All rights reserved.