Master building complete Create, Read, Update, and Delete APIs around resources. Understand collection endpoints vs. item routes, request body deserialization, status code semantics (200, 201, 204, 404), and test live operations inside an authentic in-memory interactive playground.
The Resource Lifecycle & Idempotency Principle: Standard REST CRUD APIs map HTTP methods to distinct lifecycle mutations: POST creates resources (201 Created), GET reads state (200 OK), PUT replaces or creates (200/201), PATCH mutates partial state (200 OK), and DELETE removes state (204 No Content). Always return 404 when item targets do not exist.
Connecting the 4 foundational data operations directly to standardized HTTP verbs.
Every data-driven service is built around four fundamental operations known as CRUD: Create, Read, Update, and Delete. In a REST API, these operations map cleanly to the standard HTTP request methods defined in RFC 9110:
Sends a new representation in the request body to append a new item into the collection.
Status: 201 Created
Transfers the current state representation of the collection or an individual item. Strictly read-only.
Status: 200 OK
PUT: Complete replacement of entity.
PATCH: Partial modification of specific fields.
Status: 200 OK / 204 No Content
Removes the target resource. Repeating the request is idempotent (target remains gone).
Status: 204 No Content
Collection routes, item routes, route parameters, and payload expectations.
Targets the entire set of products. No ID parameter in the URL.
GET /api/products — List all products (supports query parameters like ?limit=10).POST /api/products — Creates a new product; payload provided in request body.Targets a specific, identified product via a path parameter.
GET /api/products/:id — Fetch product by ID (404 if missing).PATCH /api/products/:id — Partially update fields.DELETE /api/products/:id — Delete product by ID./api/products/:id, accessed via req.params.id (string)./api/products/{id}, with automatic type validation: def get_product(id: int):.Complete runnable CRUD handlers using safe in-memory data structures.
const express = require('express');
const app = express();
// Middleware: Deserializes incoming application/json body into req.body
app.use(express.json());
// In-memory data store
let products = [
{ id: 1, name: 'Mechanical Keyboard', price: 99.99, stock: 15 },
{ id: 2, name: 'Wireless Mouse', price: 49.99, stock: 30 }
];
// 1. READ ALL (Collection)
app.get('/api/products', (req, res) => {
res.status(200).json(products);
});
// 2. READ ONE (Individual)
app.get('/api/products/:id', (req, res) => {
const id = Number(req.params.id);
const product = products.find(p => p.id === id);
if (!product) return res.status(404).json({ error: 'Product not found' });
res.status(200).json(product);
});
// 3. CREATE (POST)
app.post('/api/products', (req, res) => {
const { name, price, stock = 0 } = req.body;
if (!name || price === undefined) {
return res.status(400).json({ error: "Fields 'name' and 'price' are required" });
}
const newProduct = {
id: products.length > 0 ? Math.max(...products.map(p => p.id)) + 1 : 1,
name,
price: Number(price),
stock: Number(stock)
};
products.push(newProduct);
res.status(201).location(`/api/products/${newProduct.id}`).json(newProduct);
});
// 4. UPDATE (PATCH - Partial Modification)
app.patch('/api/products/:id', (req, res) => {
const id = Number(req.params.id);
const index = products.findIndex(p => p.id === id);
if (index === -1) return res.status(404).json({ error: 'Product not found' });
products[index] = { ...products[index], ...req.body, id };
res.status(200).json(products[index]);
});
// 5. DELETE (Remove)
app.delete('/api/products/:id', (req, res) => {
const id = Number(req.params.id);
const index = products.findIndex(p => p.id === id);
if (index === -1) return res.status(404).json({ error: 'Product not found' });
products.splice(index, 1);
res.status(204).end(); // 204 No Content
});
app.listen(3000, () => console.log('CRUD API listening on port 3000'));Send real HTTP requests to an active in-memory API backend and watch database records change in real time.
| ID | Product Name | Price | Stock |
|---|---|---|---|
| 1 | Mechanical Keyboard | $99.99 | 15 units |
| 2 | Wireless Ergonomic Mouse | $49.99 | 30 units |
| 3 | USB-C Dual 4K Hub | $79.99 | 8 units |
Diagnose and remediate 7 realistic bugs from real-world Express.js and FastAPI CRUD codebases.
// ❌ server.js
const express = require('express');
const app = express();
// Anti-pattern: app.use(express.json()) is missing before route handlers!
app.post('/api/products', (req, res) => {
const { name, price } = req.body; // 💥 req.body is undefined!
// ...
});Subtle scenarios developers encounter in production and how to handle them reliably.
If a client issues GET, PATCH, or DELETE for an ID that does not exist, return 404 Not Found. Never return 200 with null.
When GET /api/products has 0 records, return 200 OK with an empty JSON array []. Do NOT return 404 (the collection exists!).
The first call to DELETE /api/products/1 deletes the row and returns 204 No Content. A second identical call returns 404 Not Found because the resource is already gone.
When a client sends only {"price": 49} in PATCH, ensure all other existing attributes (name, stock, created_at) remain untouched.
Verify that an API implementation meets the full RFC 9110 specification for resources.
Run the automated test suite across all 5 CRUD operations to verify contract compliance:
Summary of foundational principles for building reliable, production-ready CRUD endpoints.
Test your knowledge of CRUD HTTP mappings, status codes, framework nuances, and edge case handling.