JSON and REST APIs

intermediate
12 min

JSON and REST APIs

REST APIs are how modern applications talk to each other, and JSON is the language they speak. Requests carry JSON bodies, responses return JSON documents, and errors arrive as JSON too. In this lesson of our JSON tutorial series, we connect everything you have learned about the format to the way real web services actually use it, including headers, status codes, pagination, and error envelopes.

The Request and Response Cycle

A typical exchange with a JSON API looks like this. The client sends:

http
POST /api/users HTTP/1.1 Host: api.example.com Content-Type: application/json Authorization: Bearer eyJhbGciOi... { "name": "Ada Lovelace", "email": "ada@example.com" }

And the server replies:

http
HTTP/1.1 201 Created Content-Type: application/json { "id": 1042, "name": "Ada Lovelace", "email": "ada@example.com", "createdAt": "2026-07-20T09:30:00Z" }

Content-Type Matters

The header Content-Type: application/json tells the receiver how to interpret the body. Forgetting it on a request is a classic bug: many frameworks will then treat your carefully stringified body as form data and fail to populate the parsed request object. Likewise, clients should check the response Content-Type before calling .json() on it, because error pages from proxies often arrive as HTML.

Consuming an API with fetch

javascript
async function createUser(user) { const res = await fetch("https://api.example.com/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.message || `HTTP ${res.status}`); } return res.json(); }

Note the two-step error handling: fetch only rejects on network failure, so you must check res.ok yourself, and the error body may not even be JSON.

Designing JSON Responses

Well-designed APIs use consistent response envelopes. Two widespread patterns:

json
{ "data": { "id": 1042, "name": "Ada" } }

for single resources, and for collections:

json
{ "data": [ { "id": 1 }, { "id": 2 } ], "pagination": { "page": 1, "perPage": 20, "total": 57 } }

Wrapping the payload in a data key leaves room for metadata and keeps the shape stable as the API evolves.

Error Responses

Errors should be JSON with a machine-readable code and a human-readable message:

json
{ "error": { "code": "VALIDATION_FAILED", "message": "email is not a valid address", "field": "email" } }

Pair the body with the correct HTTP status: 400 for bad input, 401 for missing auth, 404 for unknown resources, 422 for semantic validation failures, and 500 for server faults.

Practical Conventions

  • Use ISO 8601 UTC strings for all timestamps.
  • Send IDs that exceed 53 bits as strings to protect JavaScript clients.
  • Version your API (for example /api/v1/) so response shapes can evolve safely.
  • Support gzip or brotli compression; JSON compresses extremely well.

Key Takeaways

  • JSON is the standard body format for REST requests, responses, and errors.
  • Always set and check Content-Type: application/json.
  • Consistent envelopes with data, pagination, and structured error objects make APIs predictable.
  • Check res.ok in fetch code; HTTP errors still resolve successfully.

Next up: JSON Schema and Validation, where we make payload shapes enforceable instead of just conventional.

JSON and REST APIs - JSON | CodeYourCraft | CodeYourCraft