Step 1: RESTful API Design & Best Practices

πŸŽ₯ 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 Location header in the 201 Created response 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 Location header 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:8010 is 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 Location should 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 Location header 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

  1. Resource-Focused Design: URIs should represent resources (/users/{id}) rather than actions (/doLogin)
  2. Gateway-Only URLs: In microservices, all returned URIs must be API Gateway addresses, not internal service addresses
  3. Token-Based Authentication: Authentication should be treated as a resource (POST /auth/tokens and DELETE /auth/tokens/{id} for logout)
  4. Proper Status Codes: Use appropriate HTTP status codes to communicate the result of operations clearly
  5. Stateless Design: Each request should contain all necessary information for processing

πŸ”₯ By following these principles, your API remains RESTful, scalable, and predictable! πŸš€


Part of the AdventureTube technical blog series supporting the JavaiOS YouTube channel.

Leave a Comment

Your email address will not be published. Required fields are marked *