🍕 Building REST APIs: The Pizza Delivery Service
Imagine you run the world’s best pizza delivery service. Customers call you, tell you what they want, and you deliver delicious pizzas. REST APIs work exactly the same way – they’re how apps “order” and “deliver” data!
🎯 What You’ll Learn
- RESTful Design Principles – The golden rules of pizza ordering
- API Versioning – When your menu changes
- PUT vs PATCH – Replace the whole pizza vs. fix one topping
- Pagination & Sorting – Showing the menu 10 items at a time
- Filtering – “Show me only vegetarian pizzas”
1️⃣ RESTful Design Principles
🍕 The Pizza Analogy
Think of REST like a well-organized pizza shop:
| Pizza Shop | REST API |
|---|---|
| Menu items (pizzas) | Resources (data) |
| Order counter | Endpoints (URLs) |
| “I want to order” | HTTP Methods |
| Your order slip | Request |
| Pizza box | Response |
The 5 Golden Rules
Rule 1: Everything is a Resource
Resources are like items on your menu. Each has a unique name (URL).
/pizzas → All pizzas
/pizzas/42 → Pizza #42 (Margherita)
/customers/7 → Customer #7 (You!)
Rule 2: Use Nouns, Not Verbs
✅ Good: GET /pizzas
❌ Bad: GET /getPizzas
✅ Good: POST /orders
❌ Bad: POST /createOrder
Rule 3: HTTP Methods = Actions
graph TD A["HTTP Methods"] --> B["GET = Read"] A --> C["POST = Create"] A --> D["PUT = Replace"] A --> E["PATCH = Update"] A --> F["DELETE = Remove"]
| Action | Method | Example |
|---|---|---|
| See menu | GET | GET /pizzas |
| Order pizza | POST | POST /orders |
| Replace order | PUT | PUT /orders/1 |
| Add topping | PATCH | PATCH /orders/1 |
| Cancel order | DELETE | DELETE /orders/1 |
Rule 4: Stateless = No Memory
Every request must contain ALL the information needed. The server doesn’t remember you between requests.
❌ Server: "What pizza did you want again?"
✅ Client: "I want pizza #42, I'm customer #7,
deliver to 123 Main St"
Rule 5: Use Proper Status Codes
200 OK → "Here's your pizza! 🍕"
201 Created → "Order placed! 📝"
400 Bad Request → "We don't have that topping 🤔"
404 Not Found → "Pizza #999? Never heard of it 🔍"
500 Server Error → "Kitchen on fire! 🔥"
2️⃣ API Versioning
🍕 The Menu Update Problem
Imagine your pizza shop changes its menu:
- Old Menu: Small = 8", Medium = 10"
- New Menu: Small = 10", Medium = 12"
Old customers expecting an 8" pizza would be confused!
Versioning = Keeping old menus available while introducing new ones.
Three Ways to Version
Way 1: URL Path (Most Popular! ⭐)
/v1/pizzas → Old menu
/v2/pizzas → New menu
@RestController
@RequestMapping("/v1/pizzas")
public class PizzaControllerV1 { }
@RestController
@RequestMapping("/v2/pizzas")
public class PizzaControllerV2 { }
Way 2: Query Parameter
/pizzas?version=1 → Old menu
/pizzas?version=2 → New menu
Way 3: Header
GET /pizzas
Header: API-Version: 2
🎯 Best Practice
graph TD A["New API?"] --> B{Breaking changes?} B -->|Yes| C["New version v2"] B -->|No| D["Keep current version"] C --> E["Keep v1 running"] E --> F["Sunset v1 after 6 months"]
3️⃣ PUT vs PATCH: The Big Difference
🍕 Pizza Topping Story
You ordered a pizza with:
- Cheese ✓
- Tomatoes ✓
- Mushrooms ✓
But you want to add pepperoni…
PUT = “Replace the ENTIRE pizza”
PUT /orders/1
{
"cheese": true,
"tomatoes": true,
"mushrooms": true,
"pepperoni": true
}
You must send EVERYTHING, even things that didn’t change!
PATCH = “Just change what’s different”
PATCH /orders/1
{
"pepperoni": true
}
Only send what you’re changing!
When to Use Each
graph TD A["Need to update?"] --> B{Changing everything?} B -->|Yes, all fields| C["Use PUT"] B -->|No, just some| D["Use PATCH"] C --> E["Send complete object"] D --> F["Send only changes"]
| Scenario | Use |
|---|---|
| Update user profile completely | PUT |
| Change just the email | PATCH |
| Replace entire document | PUT |
| Toggle a setting on/off | PATCH |
Code Example
// PUT - Replace entire pizza
@PutMapping("/pizzas/{id}")
public Pizza replacePizza(
@PathVariable Long id,
@RequestBody Pizza newPizza) {
return pizzaService.replace(id, newPizza);
}
// PATCH - Update specific fields
@PatchMapping("/pizzas/{id}")
public Pizza updatePizza(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
return pizzaService.patch(id, updates);
}
4️⃣ Pagination and Sorting
🍕 The 1000 Pizza Problem
Your shop has 1000 pizzas. Would you show ALL of them at once?
NO! That’s like printing a 100-page menu. 😵
Pagination = Showing items in small groups (pages).
How Pagination Works
GET /pizzas?page=1&size=10
Returns pizzas 1-10
GET /pizzas?page=2&size=10
Returns pizzas 11-20
graph LR A["1000 Pizzas"] --> B["Page 1: 1-10"] A --> C["Page 2: 11-20"] A --> D["Page 3: 21-30"] A --> E["... 97 more pages"]
Pagination Response
{
"content": [
{"id": 1, "name": "Margherita"},
{"id": 2, "name": "Pepperoni"}
],
"page": 1,
"size": 10,
"totalElements": 1000,
"totalPages": 100,
"hasNext": true,
"hasPrevious": false
}
Adding Sorting
GET /pizzas?sort=price,asc
→ Cheapest first 💰
GET /pizzas?sort=name,desc
→ Z to A alphabetically
Spring Boot Code
@GetMapping("/pizzas")
public Page<Pizza> getPizzas(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String order) {
Sort sort = order.equals("asc")
? Sort.by(sortBy).ascending()
: Sort.by(sortBy).descending();
Pageable pageable = PageRequest.of(
page, size, sort);
return pizzaRepository.findAll(pageable);
}
5️⃣ Filtering
🍕 “I Only Want Veggie Pizzas!”
Filtering = Asking for specific items only.
GET /pizzas?vegetarian=true
→ Only veggie pizzas 🥬
GET /pizzas?price_max=15
→ Pizzas under $15 💵
GET /pizzas?vegetarian=true&price_max=15
→ Cheap veggie pizzas! 🥬💵
Common Filter Patterns
| Pattern | Example | Meaning |
|---|---|---|
| Exact match | ?status=available |
Only available |
| Range | ?price_min=5&price_max=20 |
$5-$20 |
| Contains | ?name_like=cheese |
Has “cheese” |
| Multiple values | ?size=small,medium |
Small OR medium |
Spring Boot Filtering
@GetMapping("/pizzas")
public List<Pizza> getPizzas(
@RequestParam(required = false)
Boolean vegetarian,
@RequestParam(required = false)
Double priceMax,
@RequestParam(required = false)
String size) {
Specification<Pizza> spec =
Specification.where(null);
if (vegetarian != null) {
spec = spec.and(
(root, q, cb) ->
cb.equal(root.get("vegetarian"),
vegetarian));
}
if (priceMax != null) {
spec = spec.and(
(root, q, cb) ->
cb.lessThanOrEqualTo(
root.get("price"), priceMax));
}
return pizzaRepository.findAll(spec);
}
Combining Everything!
GET /v2/pizzas
?vegetarian=true
&price_max=20
&page=1
&size=10
&sort=price,asc
Translation: “From version 2 of the API, show me the first 10 vegetarian pizzas under $20, sorted by price from cheapest to most expensive.”
🎉 Congratulations!
You now understand REST APIs like a pizza delivery pro!
graph LR A["REST API Master"] --> B["RESTful Principles"] A --> C["Versioning"] A --> D["PUT vs PATCH"] A --> E["Pagination & Sorting"] A --> F["Filtering"] B --> G["Resources + Methods + Status Codes"] C --> H["v1, v2 in URLs"] D --> I["Replace vs Update"] E --> J["page + size + sort"] F --> K["Query parameters"]
Quick Reference
| Concept | Remember |
|---|---|
| REST Resources | Nouns in URLs (/pizzas) |
| HTTP Methods | GET, POST, PUT, PATCH, DELETE |
| Versioning | /v1/, /v2/ in URL path |
| PUT | Replace EVERYTHING |
| PATCH | Update SOME things |
| Pagination | ?page=1&size=10 |
| Sorting | ?sort=price,asc |
| Filtering | ?vegetarian=true |
🍕 “REST APIs are just polite conversations between apps. Be clear about what you want, and you’ll always get the right pizza!”
