HTTP Methods and JSON
Yesterday you built a server that responds to requests. Today you learn the different types of requests (HTTP methods) and JSON - the universal data format that APIs use to communicate.
HTTP Methods - The Verbs of the Web
HTTP methods tell the server what you want to do. Think of them as verbs - actions you want to perform on a resource.
The Main HTTP Methods
- GET - Retrieve data (Read)
- POST - Create new data (Create)
- PUT - Update existing data completely (Replace)
- PATCH - Update existing data partially (Modify)
- DELETE - Remove data (Delete)
Think of a library. GET is reading a book. POST is donating a new book. PUT is replacing a book with a new edition. PATCH is fixing a typo in a book. DELETE is removing a book from the collection.
GET Requests
GET retrieves data without modifying anything. It is the most common request - every time you visit a URL, your browser makes a GET request.
# Get all users
GET /api/users
# Get a specific user by ID
GET /api/users/123
# Get users with query parameters (filtering)
GET /api/users?role=admin&active=true
# Get users with pagination
GET /api/users?page=2&limit=10
GET requests should NEVER modify data. They should be idempotent - calling them multiple times produces the same result. GET requests also should not have a request body.
POST Requests
POST creates new resources. It sends data in the request body and typically returns the created resource.
# Create a new user
POST /api/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "secret123"
}
# Response: 201 Created
{
"id": 456,
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
PUT Requests
PUT replaces an entire resource. You send the complete new version.
# Replace user 456 completely
PUT /api/users/456
Content-Type: application/json
{
"name": "John Smith",
"email": "johnsmith@example.com",
"role": "admin"
}
# Response: 200 OK with updated resource
PATCH Requests
PATCH updates only specific fields, leaving others unchanged.
# Update ONLY the email of user 456
PATCH /api/users/456
Content-Type: application/json
{
"email": "newemail@example.com"
}
# Other fields (name, role) remain unchanged
DELETE Requests
DELETE removes a resource. It usually returns 204 No Content on success.
# Delete user 456
DELETE /api/users/456
# Response: 204 No Content (success, no body)
# Or: 200 OK with { "message": "User deleted" }
What is JSON?
JSON (JavaScript Object Notation) is a text format for storing and transmitting data. It is the standard format for APIs because it is:
- Human-readable and writable
- Easy for machines to parse and generate
- Language-independent (works everywhere)
- Lightweight compared to XML
{
"string": "Hello World", // Text in double quotes
"number": 42, // Integer or decimal
"decimal": 3.14159, // Floating point
"boolean": true, // true or false (lowercase!)
"nullValue": null, // null (lowercase!)
"array": [1, 2, 3, "mixed", true], // Ordered list
"object": { // Nested object
"nested": "value",
"deep": {
"level": 3
}
}
}
Keys must be strings in double quotes. No trailing commas. No comments (unlike JavaScript). Strings must use double quotes, not single. Boolean and null are lowercase.
JSON in Node.js
// JavaScript object
const user = {
name: 'John',
age: 30,
hobbies: ['coding', 'gaming']
};
// Convert JavaScript object to JSON string
const jsonString = JSON.stringify(user);
console.log(jsonString);
// Output: {"name":"John","age":30,"hobbies":["coding","gaming"]}
// Pretty print with indentation
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
// Output:
// {
// "name": "John",
// "age": 30,
// "hobbies": ["coding", "gaming"]
// }
// Convert JSON string back to JavaScript object
const parsed = JSON.parse(jsonString);
console.log(parsed.name); // John
console.log(parsed.hobbies[0]); // coding
Handling POST Requests with JSON
In raw Node.js, request bodies come in chunks that you need to collect. Here is how:
const http = require('http');
// Simple in-memory database
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
let nextId = 3;
const server = http.createServer((req, res) => {
const { method, url } = req;
// Set JSON response headers
res.setHeader('Content-Type', 'application/json');
// GET /api/users - Return all users
if (method === 'GET' && url === '/api/users') {
res.statusCode = 200;
res.end(JSON.stringify(users));
return;
}
// POST /api/users - Create a new user
if (method === 'POST' && url === '/api/users') {
let body = '';
// Collect data chunks as they arrive
req.on('data', (chunk) => {
body += chunk.toString();
});
// All data received
req.on('end', () => {
try {
// Parse JSON body
const data = JSON.parse(body);
// Validate required fields
if (!data.name || !data.email) {
res.statusCode = 400;
res.end(JSON.stringify({
error: 'Name and email are required'
}));
return;
}
// Create new user
const newUser = {
id: nextId++,
name: data.name,
email: data.email
};
// Add to our "database"
users.push(newUser);
// Return created user with 201 status
res.statusCode = 201;
res.end(JSON.stringify(newUser));
} catch (error) {
// Invalid JSON
res.statusCode = 400;
res.end(JSON.stringify({
error: 'Invalid JSON'
}));
}
});
return;
}
// 404 for unmatched routes
res.statusCode = 404;
res.end(JSON.stringify({ error: 'Not Found' }));
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Testing Your API
Use these tools to test your API endpoints:
# GET all users
curl http://localhost:3000/api/users
# POST - Create new user
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Charlie", "email": "charlie@test.com"}'
# Pretty print JSON output
curl http://localhost:3000/api/users | json_pp
API Testing Tools
- Postman - Popular GUI tool for API testing
- Insomnia - Clean, fast alternative to Postman
- Thunder Client - VS Code extension
- cURL - Command line tool (built into Mac/Linux)
- HTTPie - User-friendly command line tool
Complete CRUD Server
Here is a complete server with all HTTP methods:
const http = require('http');
let todos = [
{ id: 1, task: 'Learn Node.js', done: false },
{ id: 2, task: 'Build an API', done: false }
];
let nextId = 3;
// Helper to parse JSON body
function getBody(req) {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
resolve(body ? JSON.parse(body) : {});
} catch (e) {
reject(e);
}
});
});
}
// Helper to send JSON response
function sendJson(res, status, data) {
res.statusCode = status;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(data));
}
const server = http.createServer(async (req, res) => {
const { method, url } = req;
// Parse URL to get ID: /api/todos/123 -> 123
const match = url.match(/^\/api\/todos\/(\d+)$/);
const id = match ? parseInt(match[1]) : null;
// GET /api/todos - List all todos
if (method === 'GET' && url === '/api/todos') {
sendJson(res, 200, todos);
return;
}
// GET /api/todos/:id - Get single todo
if (method === 'GET' && id) {
const todo = todos.find(t => t.id === id);
if (!todo) {
sendJson(res, 404, { error: 'Todo not found' });
return;
}
sendJson(res, 200, todo);
return;
}
// POST /api/todos - Create todo
if (method === 'POST' && url === '/api/todos') {
try {
const data = await getBody(req);
if (!data.task) {
sendJson(res, 400, { error: 'Task is required' });
return;
}
const newTodo = {
id: nextId++,
task: data.task,
done: false
};
todos.push(newTodo);
sendJson(res, 201, newTodo);
} catch (e) {
sendJson(res, 400, { error: 'Invalid JSON' });
}
return;
}
// PUT /api/todos/:id - Update todo
if (method === 'PUT' && id) {
const index = todos.findIndex(t => t.id === id);
if (index === -1) {
sendJson(res, 404, { error: 'Todo not found' });
return;
}
try {
const data = await getBody(req);
todos[index] = { ...todos[index], ...data };
sendJson(res, 200, todos[index]);
} catch (e) {
sendJson(res, 400, { error: 'Invalid JSON' });
}
return;
}
// DELETE /api/todos/:id - Delete todo
if (method === 'DELETE' && id) {
const index = todos.findIndex(t => t.id === id);
if (index === -1) {
sendJson(res, 404, { error: 'Todo not found' });
return;
}
todos.splice(index, 1);
sendJson(res, 200, { message: 'Todo deleted' });
return;
}
// 404 for everything else
sendJson(res, 404, { error: 'Not Found' });
});
server.listen(3000, () => {
console.log('Todo API running on http://localhost:3000');
});
Video Resources
Knowledge Check: Day 3
1. Which HTTP method should you use to create a new resource?
2. What does JSON.stringify() do?
3. What is the difference between PUT and PATCH?
4. What Content-Type header should you set for JSON responses?
Day 3 Complete!
You now understand HTTP methods and can build APIs that Create, Read, Update, and Delete data. You also know JSON - the language APIs speak. This CRUD pattern is the foundation of almost every web application.
Tomorrow, we introduce Express.js - a framework that makes building servers much easier and more organized.