DEV Community

DevCorner
DevCorner

Posted on

REST API URL Structure: Best Practices with Spring Boot Example

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Header Versioning
GET /users
Accept: application/vnd.example.v1+json
Enter fullscreen mode Exit fullscreen mode
  1. Query Parameter Versioning
GET /users?version=1
Enter fullscreen mode Exit fullscreen mode

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  
Enter fullscreen mode Exit fullscreen mode

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  
Enter fullscreen mode Exit fullscreen mode

D. Query Parameters for Filtering, Sorting, and Pagination

Filtering:

GET /v1/products?category=electronics&brand=apple
Enter fullscreen mode Exit fullscreen mode

Sorting:

GET /v1/products?sort=price_asc
Enter fullscreen mode Exit fullscreen mode

Pagination:

GET /v1/products?page=2&limit=20
Enter fullscreen mode Exit fullscreen mode

Offset-based Pagination:

GET /v1/products?offset=40&limit=20
Enter fullscreen mode Exit fullscreen mode

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  
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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! 🚀

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed
  • 2:34 --only-changed
  • 4:27 --repeat-each
  • 5:15 --forbid-only
  • 5:51 --ui --headed --workers 1

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

Build With Me: AI-Powered Adaptive Web Scraper with LLMs

Join us for a hands-on session with Zia Ahmad where we build an AI-driven web scraper that adapts to site changes in real-time. Code along and level up your automation skills.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️