🌐 Step 1: RESTful API Design & Best Practices
🧭 Series Navigation:
- ▶️ Backend Development Fundamentals Hub
- ✅ Step 1: RESTful API Design & Best Practices (Current)
- ▶️ Step 2: URI Construction & API Response Patterns
- ▶️ Step 3: Maven Dependency Management
- ▶️ Step 4: Eureka Service Registration & Discovery
- ▶️ Step 5: Testing Tools & Authentication Setup
🎥 YouTube Video: Coming Soon – Subscribe to JavaiOS Channel
Understanding RESTful APIs and Fixing Common Mistakes 🌐
This document covers key concepts of RESTful APIs, the mistakes made in API design, why they were problematic, and how they were fixed. Learn from real-world examples in the AdventureTube microservice architecture.
🎯 What is a RESTful API?
A RESTful API (Representational State Transfer API) is an architectural style for designing web services that follow a structured and predictable way of interacting with resources over HTTP.
🏗️ Key Principles of RESTful APIs
| Principle | Description |
|---|---|
| Client-Server | Separates client (frontend, mobile) and server (backend, database). |
| Stateless | Each request contains all needed information. The server doesn’t store session state. |
| Cacheable | Responses can indicate whether they can be cached to improve performance. |
| Uniform Interface | Consistent structure using HTTP methods (GET, POST, PUT, DELETE). |
| Layered System | The API can work across multiple layers (security, caching, database) transparently. |
| Code on Demand (Optional) | API can return executable code (e.g., JavaScript) to run on the client side. |
🚀 Why Should APIs Be RESTful?
- ✅ Scalability – Stateless APIs handle more concurrent requests
- ✅ Predictability – Standard HTTP methods make the API easy to understand
- ✅ Maintainability – Clean, resource-based URIs make debugging easier
- ✅ Interoperability – Any system can use RESTful APIs, regardless of technology
- ✅ Faster Development – Consistency speeds up frontend-backend integration
❌ Mistake #1: Non-RESTful URI Design
🚨 Old (Incorrect) Code
URI uri = URI.create(ServletUriComponentsBuilder.fromCurrentContextPath() .path("/auth/register") .toUriString()); return ResponseEntity.created(uri).body(authService.register(request));
What’s Wrong?
🚨 Issue:
- The
Locationheader in the201 Createdresponse points to the registration endpoint (/auth/register) instead of the newly created user resource - The client has no way to directly access the created user
- Breaks REST principles by not linking resources properly
Incorrect Response Header
POST /register Location: http://192.168.1.112:8010/auth/register
🚨 Problem: The Location should point to the newly created user (/users/{id}), not the registration endpoint.
✅ Updated (Correct) Code
URI uri = URI.create(ServletUriComponentsBuilder.fromCurrentContextPath() .path("/users/{id}") .buildAndExpand(response.getUserId()) // Replacing {id} with the actual user ID .toUriString()); return ResponseEntity.created(uri).body(response);
✅ Fixes:
- The
Locationheader now correctly points to the created user (/users/{id}) - The client can fetch the user directly using
GET /users/{id} - The API now properly links resources instead of endpoints
Correct Response Header
POST /register Location: http://192.168.1.112:8010/users/8311471e-3845-49e2-90b9-826f338efc74
✅ Now, the client knows where to find the new user.
❌ Mistake #2: Returning an Inaccessible Microservice URL
🚨 Problem: Wrong Location Header with Internal IP
Even after fixing the resource-based URL, another mistake was found: the Location header returned an internal microservice IP instead of the API Gateway URL.
Incorrect Response (Not Accessible by Client)
HTTP/1.1 201 Created Location: http://192.168.1.112:8010/users/8311471e-3845-49e2-90b9-826f338efc74
🚨 Issue:
192.168.1.112:8010is an internal microservice IP, not accessible externally- The client can’t use this address, as the microservices are only accessible via an API Gateway
- The correct
Locationshould point to the API Gateway, not the microservice
✅ Correct Fix: Return the API Gateway URL
String gatewayBaseUrl = "https://api.adventuretube.net"; // API Gateway URL URI uri = URI.create(gatewayBaseUrl + "/users/" + response.getUserId()); return ResponseEntity.created(uri).body(response);
✅ Fixes:
- Now the
Locationheader points to the API Gateway (api.adventuretube.net) instead of an internal IP - The client can actually access the created resource
- Microservices remain hidden and secured
Correct Response Header (Client-Accessible)
HTTP/1.1 201 Created Location: https://api.adventuretube.net/users/8311471e-3845-49e2-90b9-826f338efc74
✅ Now the client can access the resource through the API Gateway.
❌ Mistake #3: Action-Based Login Instead of Token-Based Authentication
🚨 Problem: Using POST /doLogin Instead of POST /auth/tokens
🚨 Issue:
- Login was handled as an action (
POST /doLogin) instead of treating authentication as a resource - This breaks RESTful principles because authentication should be a resource (
auth/tokens), not an action
✅ Correct Fix: Token-Based Authentication
Request for Token Creation
POST /auth/tokens Content-Type: application/json { "email": "strider@adventuretube.net", "googleIdToken": "eyJhbGciOiJSUzI1NiIs...", "googleId": "12345" }
Server Response with Authentication Token
HTTP/1.1 201 Created Location: https://api.adventuretube.net/auth/tokens/abcd1234xyz { "accessToken": "eyJhbGciOiJIUzUxMiJ9...", "refreshToken": "def5678uvw...", "expiresIn": 3600, "tokenType": "Bearer" }
Using the Token for Secured Requests
GET /users/123 Authorization: Bearer eyJhbGciOiJIUzUxMiJ9...
✅ This follows stateless authentication and is fully RESTful.
🎯 REST API Design Best Practices
1. Resource-Based URLs
| ❌ Wrong (Action-Based) | ✅ Correct (Resource-Based) |
|---|---|
POST /doLogin |
POST /auth/tokens |
POST /createUser |
POST /users |
GET /getUserById/123 |
GET /users/123 |
POST /deleteUser/123 |
DELETE /users/123 |
2. HTTP Status Codes
| Status Code | Usage | Example |
|---|---|---|
200 OK |
Successful GET, PUT, PATCH | User data retrieved successfully |
201 Created |
Successful POST (resource created) | New user registered |
204 No Content |
Successful DELETE | User deleted successfully |
400 Bad Request |
Invalid request data | Missing required fields |
401 Unauthorized |
Authentication required | Invalid or missing token |
403 Forbidden |
Access denied | Insufficient permissions |
404 Not Found |
Resource doesn’t exist | User ID not found |
500 Internal Server Error |
Server-side error | Database connection failed |
3. HTTP Methods Usage
| Method | Purpose | Idempotent | Example |
|---|---|---|---|
GET |
Retrieve data | Yes | GET /users/123 |
POST |
Create new resource | No | POST /users |
PUT |
Update entire resource | Yes | PUT /users/123 |
PATCH |
Partial update | No | PATCH /users/123 |
DELETE |
Remove resource | Yes | DELETE /users/123 |
🔧 Implementation Examples from AdventureTube
User Registration Endpoint
@PostMapping("/auth/register") public ResponseEntity registerUser(@RequestBody SignupRequest request) { AuthResponse response = authService.createUser(request); // Correct: Point to the created user resource URI uri = URI.create(gatewayBaseUrl + "/users/" + response.getUserId()); return ResponseEntity.created(uri).body(response); }
Token Authentication Endpoint
@PostMapping("/auth/tokens") public ResponseEntity issueToken(@RequestBody AuthRequest request) { AuthResponse response = authService.issueToken(request); // RESTful: Treat token as a resource URI uri = URI.create(gatewayBaseUrl + "/auth/tokens/" + response.getTokenId()); return ResponseEntity.created(uri).body(response); }
User Retrieval Endpoint
@GetMapping("/users/{userId}") public ResponseEntity getUser(@PathVariable String userId) { UserResponse user = userService.findById(userId); if (user == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(user); }
✅ RESTful API Checklist
URL Design ✅
- ✅ Use nouns for resources, not verbs
- ✅ Use plural nouns for collections (
/users) - ✅ Use hierarchical structure (
/users/{id}/orders) - ✅ Avoid deep nesting (max 2-3 levels)
HTTP Methods ✅
- ✅ GET for data retrieval (no side effects)
- ✅ POST for resource creation
- ✅ PUT for complete resource updates
- ✅ PATCH for partial updates
- ✅ DELETE for resource removal
Response Design ✅
- ✅ Appropriate HTTP status codes
- ✅ Consistent JSON response structure
- ✅ Location header for created resources
- ✅ Meaningful error messages
Security & Gateway ✅
- ✅ Use API Gateway URLs in responses
- ✅ Hide internal microservice details
- ✅ Implement stateless authentication
- ✅ Use HTTPS for all communications
🎓 Key Takeaways
- Resource-Focused Design: URIs should represent resources (
/users/{id}) rather than actions (/doLogin) - Gateway-Only URLs: In microservices, all returned URIs must be API Gateway addresses, not internal service addresses
- Token-Based Authentication: Authentication should be treated as a resource (
POST /auth/tokensandDELETE /auth/tokens/{id}for logout) - Proper Status Codes: Use appropriate HTTP status codes to communicate the result of operations clearly
- Stateless Design: Each request should contain all necessary information for processing
🔥 By following these principles, your API remains RESTful, scalable, and predictable! 🚀
🚀 Continue Your Backend Development Journey
🧭 Next Steps in Backend Development Fundamentals:
- ▶️ Backend Development Fundamentals Hub
- ✅ Step 1: RESTful API Design & Best Practices (Current)
- ▶️ Step 2: URI Construction & API Response Patterns
- ▶️ Step 3: Maven Dependency Management
- ▶️ Step 4: Eureka Service Registration & Discovery
- ▶️ Step 5: Testing Tools & Authentication Setup
Part of the AdventureTube technical blog series supporting the JavaiOS YouTube channel.
