Introduction

A well-designed REST API URL structure is crucial for building scalable, readable, and maintainable APIs. In this blog, we will break down the components of a REST API URL, discuss best practices, and implement them in Spring Boot with a complete example.


1. General REST API URL Structure

A standard REST API URL follows this pattern:

https://api.example.com/{version}/{resource}/{resource-id}/{sub-resource}

Breakdown:

  • https:// → Secure protocol (HTTPS is preferred)
  • api.example.com → Domain name or API host
  • {version} → API versioning (e.g., v1, v2)
  • {resource} → Plural noun representing the resource (e.g., users, products)
  • {resource-id} → Unique identifier for a resource (e.g., users/123)
  • {sub-resource} → Nested resource related to the main resource (e.g., users/123/orders)

2. Components of REST API URL

A. Versioning in REST API

Versioning ensures backward compatibility and can be implemented in different ways:

  1. URL Path Versioning (Recommended)
https://api.example.com/v1/users
  1. Header Versioning
GET /users
Accept: application/vnd.example.v1+json
  1. Query Parameter Versioning
GET /users?version=1

B. Resource Naming (Use Plural Nouns)

Always use plural nouns for resources:

GET /v1/users      → Get all users  
GET /v1/users/123  → Get user with ID 123  
POST /v1/users     → Create a new user  
PUT /v1/users/123  → Update user 123  
DELETE /v1/users/123 → Delete user 123

C. Sub-Resources for Relationships

Use sub-resources to represent relationships:

GET /v1/users/123/orders → Get orders of user 123  
GET /v1/users/123/orders/456 → Get order 456 of user 123

D. Query Parameters for Filtering, Sorting, and Pagination

Filtering:

GET /v1/products?category=electronics&brand=apple

Sorting:

GET /v1/products?sort=price_asc

Pagination:

GET /v1/products?page=2&limit=20

Offset-based Pagination:

GET /v1/products?offset=40&limit=20

E. Actions that Don’t Fit CRUD (Use Verbs as Subpaths)

For actions beyond CRUD, use verbs:

POST /v1/users/123/activate → Activate user 123  
POST /v1/orders/789/cancel → Cancel order 789

F. Status and Error Handling

Use appropriate HTTP status codes:

  • 200 OK → Successful request
  • 201 Created → Resource created
  • 400 Bad Request → Client error
  • 404 Not Found → Resource not found
  • 500 Internal Server Error → Server issue

3. Best Practices for REST API URL Design

✅ Use nouns for resources (/users instead of /getUsers)
✅ Use hyphens (-) instead of underscores (_) in URLs (/user-profiles not /user_profiles)
✅ Use lowercase letters (/orders instead of /Orders)
✅ Avoid file extensions (/users.json is unnecessary)
✅ Keep URLs short and intuitive


4. Spring Boot Implementation

Let's implement these best practices in a Spring Boot REST API for managing users and orders.

A. User Controller with Versioning, Pagination, Filtering, Offset-based Pagination, and Sorting

@RestController
@RequestMapping("/v1/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers(
            @RequestParam(required = false) String name,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "id") String sortBy,
            @RequestParam(defaultValue = "asc") String sortDir,
            @RequestParam(required = false) Integer offset) {

        Sort sort = sortDir.equalsIgnoreCase("desc") ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
        Pageable pageable = offset != null ? PageRequest.of(offset / size, size, sort) : PageRequest.of(page, size, sort);

        Page<User> users;
        if (name != null) {
            users = userRepository.findByNameContaining(name, pageable);
        } else {
            users = userRepository.findAll(pageable);
        }

        return ResponseEntity.ok(users.getContent());
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userRepository.findById(id)
                .map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        return ResponseEntity.status(HttpStatus.CREATED).body(userRepository.save(user));
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
        return userRepository.findById(id).map(user -> {
            user.setName(updatedUser.getName());
            user.setEmail(updatedUser.getEmail());
            return ResponseEntity.ok(userRepository.save(user));
        }).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userRepository.deleteById(id);
        return ResponseEntity.noContent().build();
    }
}

Conclusion

Designing a RESTful API URL properly improves usability, scalability, and maintainability. By following best practices and implementing them in Spring Boot, you can build high-quality, developer-friendly APIs with versioning, pagination, offset-based pagination, sorting, and filtering.

Would you like to extend this with authentication or advanced query capabilities? Let me know in the comments! 🚀