Skip to main content

Command Palette

Search for a command to run...

📦 JSON — The Language APIs Speak

Updated
11 min read

🤔 Why JSON Matters for Every DevOps Engineer

In Part 3 you saw {"id":42,"name":"Laptop","price":75000} appear in every curl response. That format is called JSON, and it is everywhere:

  • Every REST API request and response body

  • AWS CLI output (--output json)

  • Configuration files (package.json, tsconfig.json)

  • CloudWatch logs, ECS task definitions, IAM policies

  • Database columns in PostgreSQL (JSONB type)

If you can read and write JSON fluently, you can debug API responses, write AWS CLI queries, edit config files, and query JSON columns in your database — all using the same skill.

This post teaches you JSON completely, from the absolute basics to querying it from your terminal with jq.

📝 What Is JSON?

JSON stands for JavaScript Object Notation. Despite the name, it has nothing to do with running JavaScript — it is just a text format for representing structured data, and almost every programming language can read and write it.

Think of JSON as a more machine-friendly version of a form. A human fills out a form with labeled fields. JSON represents the exact same information, but in a format a computer can parse instantly.

{
  "name": "Rajaram",
  "email": "raj@example.com",
  "role": "DevOps Engineer",
  "skills": ["AWS", "Linux", "Docker"],
  "active": true
}

This is valid JSON. Notice:

  • Curly braces { } wrap the whole object

  • Each field is a "key": value pair

  • Keys are always in double quotes

  • Commas separate fields — but no comma after the last one

# See JSON in action right now
curl -s https://jsonplaceholder.typicode.com/users/1

# Pretty-print it (much easier to read)
curl -s https://jsonplaceholder.typicode.com/users/1 | python3 -m json.tool

🔢 The 6 Data Types — That's All There Is

JSON has exactly six data types. Once you know them, you can read any JSON document, no matter how complex.

Type Example Notes
String "name": "Rajaram" Text, always in double quotes
Number "age": 30, "price": 75000.50 Integers AND decimals, no quotes
Boolean "active": true Only true or false, no quotes
null "middleName": null Means "no value" — different from "" (empty string)
Array "skills": ["AWS","Linux"] Ordered list, square brackets [ ]
Object "address": {"city":"Bangalore"} Nested key-value pairs, curly braces { }

The most common beginner confusion: null vs "" vs missing field

{
  "middleName": null,        // field exists, value is "nothing"
  "nickname": "",            // field exists, value is empty text
  // "lastLogin" is not here at all — field doesn't exist
}

All three look similar but mean different things to your code. null usually means "this was never set." An empty string means "this was set, but to nothing."

# Check the type of a value with jq
echo '{"age": 30, "active": true, "name": "Raj"}' | jq '.age | type'
# Output: "number"

echo '{"age": 30, "active": true, "name": "Raj"}' | jq '.active | type'
# Output: "boolean"

🪆 Nested Objects and Arrays — Reading Real API Responses

Real-world API responses are rarely flat. They have objects inside objects, and arrays full of objects. This is where beginners get lost — but the rule is simple: read it one level at a time.

{
  "id": 1001,
  "customer": {
    "name": "Rajaram",
    "email": "raj@example.com"
  },
  "items": [
    { "product": "Laptop", "qty": 1 },
    { "product": "Mouse", "qty": 2 }
  ]
}

How to read this:

  • id is a top-level field — a simple number

  • customer is an object — it has its own fields inside { }

  • items is an array — a list of objects, each with product and qty

To get specific values, you walk down the path:

order.customer.name       → "Rajaram"
order.items[0].product    → "Laptop"   (first item, index 0)
order.items[1].qty         → 2          (second item, index 1)

💡 Arrays start counting from 0, not 1. items[0] is the first item, items[1] is the second. This trips up everyone at first

# Practice walking a nested path with jq
echo '{"customer":{"name":"Rajaram"},"items":[{"product":"Laptop"},{"product":"Mouse"}]}' \
  | jq '.customer.name'
# Output: "Rajaram"

echo '{"customer":{"name":"Rajaram"},"items":[{"product":"Laptop"},{"product":"Mouse"}]}' \
  | jq '.items[1].product'
# Output: "Mouse"

⚔️ JSON vs XML — Why JSON Won

Before JSON became standard, APIs used XML. You may still encounter XML in older enterprise systems or SOAP-based APIs.

<!-- XML — verbose -->
<user>
  <name>Rajaram</name>
  <role>DevOps</role>
  <skills>
    <skill>AWS</skill>
    <skill>Linux</skill>
  </skills>
</user>
// JSON — same data, fewer characters
{
  "name": "Rajaram",
  "role": "DevOps",
  "skills": ["AWS", "Linux"]
}

JSON won because it is shorter, maps directly to data structures every programming language already has (objects and arrays), and is faster to parse. Today, almost every REST API uses JSON. XML still appears in SOAP APIs, RSS feeds, and some legacy enterprise systems — but for new projects, JSON is the default.

🔍 jq — Query JSON From Your Terminal

jq is a command-line tool for working with JSON. If grep is for searching text, jq is for searching and transforming JSON. Every DevOps engineer should have this in their toolkit.

# Install jq if you don't have it
sudo apt install jq        # Ubuntu/Debian
brew install jq            # macOS

The essential jq commands:

# Sample data
echo '{"name":"Rajaram","role":"DevOps","skills":["AWS","Linux","Docker"]}' > sample.json

# Pretty-print (formats messy one-line JSON into readable form)
jq '.' sample.json

# Get one field
jq '.name' sample.json
# Output: "Rajaram"

# Get raw output without quotes (useful in scripts)
jq -r '.name' sample.json
# Output: Rajaram

# Get an array element by index
jq '.skills[0]' sample.json
# Output: "AWS"

# Count items in an array
jq '.skills | length' sample.json
# Output: 3

Real pipeline — combining curl and jq:

# Get just the name from an API response
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.name'

# Get only the city from a nested address object
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.address.city'

# Filter an array — get only users whose id is greater than 5
curl -s https://jsonplaceholder.typicode.com/users | jq '.[] | select(.id > 5) | .name'

# Use in a script — store a value in a shell variable
USER_NAME=$(curl -s https://jsonplaceholder.typicode.com/users/1 | jq -r '.name')
echo "Hello, $USER_NAME"

Where you will use this daily:

# AWS CLI output is JSON by default — pipe it through jq
aws ec2 describe-instances | jq '.Reservations[].Instances[].InstanceId'

# Get just the running instance IDs
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  | jq -r '.Reservations[].Instances[].InstanceId'

🗄️ JSON in PostgreSQL — Databases Speak JSON Too

You do not need a separate "NoSQL" database to work with JSON. PostgreSQL (and most modern relational databases) has a native JSONB column type that stores JSON efficiently and lets you query inside it with SQL.

-- Create a table with a JSONB column
CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT,
  attributes JSONB
);

-- Insert a row with JSON data
INSERT INTO products (name, attributes) VALUES
  ('Laptop', '{"color":"silver","ram_gb":16,"in_stock":true}');

-- Query a specific key inside the JSON
SELECT attributes->'ram_gb' FROM products WHERE id = 1;
-- Returns: 16

-- Query using ->> to get text (not JSON) output
SELECT name FROM products WHERE attributes->>'color' = 'silver';

-- Combine JSON conditions with regular SQL conditions
SELECT name FROM products
WHERE attributes->>'color' = 'silver'
  AND (attributes->>'in_stock')::boolean = true;

Why this matters: Your application can store flexible, evolving data (like product attributes that differ per category) without needing a schema migration every time a new attribute appears — while still being able to query it efficiently with SQL. We will go much deeper into SQL in Part 14 and Part 15.

# From your terminal, connect and try this
psql -h your-db-host -U your-user -d your-database

# Then run:
\d products
SELECT name, attributes->'ram_gb' AS ram FROM products;

⚠️ JSON Mistakes — The 5 Errors Everyone Makes

A single missing comma or wrong quote type turns valid JSON into an error. Here are the five mistakes that cause 90% of "JSON parse error" issues.

1. Single quotes instead of double quotes

❌ {'name': 'Rajaram'}
✅ {"name": "Rajaram"}

JSON requires double quotes for both keys and string values. Single quotes are valid in JavaScript objects but not in JSON.

2. Trailing comma

❌ {"name":"Raj", "role":"DevOps",}
✅ {"name":"Raj", "role":"DevOps"}

No comma after the last item in an object or array.

3. Unquoted keys

❌ {name: "Rajaram"}
✅ {"name": "Rajaram"}

Every key must be a string in double quotes

4. Comments in JSON

❌ {"name":"Raj" // this is the user's name}
✅ {"name":"Raj"}

JSON has no comment syntax. Remove comments entirely. (Some config formats like jsonc allow comments, but plain JSON does not.)

5. Forgetting Content-Type: application/json

❌ curl -X POST https://api.example.com/users -d '{"name":"Raj"}'
✅ curl -X POST https://api.example.com/users \
     -H "Content-Type: application/json" \
     -d '{"name":"Raj"}'

Without this header, the server may not parse your body as JSON and could return 400 Bad Request.

💡 Validate before sending: echo '{"name":"Raj"}' | jq '.' — if jq prints the formatted JSON, it is valid. If it errors with parse error, fix your JSON before sending it anywhere.

🛠️ Hands-On Lab — Practice JSON From Your Terminal

# 1. Fetch and pretty-print a real API response
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.'

# 2. Get a single field
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.email'

# 3. Get a nested field
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.address.city'

# 4. Get an array's length
curl -s https://jsonplaceholder.typicode.com/users | jq 'length'

# 5. Loop through an array and get one field from each item
curl -s https://jsonplaceholder.typicode.com/users | jq '.[].name'

# 6. Filter — only users from a specific city
curl -s https://jsonplaceholder.typicode.com/users | jq '.[] | select(.address.city=="Gwenborough")'

# 7. Validate a JSON string
echo '{"name":"Rajaram","valid":true}' | jq '.'

# 8. Find the broken JSON (intentionally invalid)
echo '{name:"Rajaram"}' | jq '.'
# This will error — unquoted key "name"

# 9. Send JSON with curl
curl -X POST https://jsonplaceholder.typicode.com/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"Test","body":"Hello","userId":1}' | jq '.'

📖 Quick Reference Cheat Sheet

Concept Plain English
String Text in double quotes: "hello"
Number No quotes: 42, 3.14
Boolean Only true or false, no quotes
null Means "no value" — not the same as ""
Array Ordered list in [ ]
Object Key-value pairs in { }
Path syntax order.customer.name, items[0].product
jq '.' Pretty-print JSON
jq '.field' Get one field
jq -r Raw output, no quotes
**jq '.[] select(...)'**
JSONB PostgreSQL's native JSON column type
-> Get JSON value (PostgreSQL)
->> Get text value (PostgreSQL)
Content-Type Always send application/json when posting JSON

✅ What You Learned Today

✅ JSON is a text format with exactly 6 data types: string, number, boolean, null, array, object

null means "no value" — different from an empty string ""

✅ Nested data is read one level at a time: customer.name, items[0].product

✅ Arrays start at index 0, not 1

✅ JSON replaced XML because it is shorter and maps directly to code data structures

jq lets you query, filter, and extract values from JSON in your terminal

curl ... | jq '.field' is a pattern you will use constantly

✅ PostgreSQL's JSONB column lets you store and query JSON with SQL using -> and ->>

✅ The 5 common mistakes: single quotes, trailing commas, unquoted keys, comments, missing Content-Type

📚 References and Further Reading

⏭️ Up Next — Part 5: REST API Fundamentals 🔗

You now know HTTP and JSON — the two building blocks of every API. Part 5 puts them together: what REST actually means, how resources and endpoints are designed, CRUD operations, and idempotency — with real-world examples from the GitHub and AWS APIs