API versioning strategies

David Kumar Jan 2026
4 tabs
package com.example.demo.controller;

import com.example.demo.dto.UserDTO;
import com.example.demo.dto.v2.UserDTOV2;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {

    private final UserService userService;

    public UserControllerV1(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List<UserDTO> getUsers() {
        return userService.getAllUsersV1();
    }

    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserV1(id);
    }
}

@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {

    private final UserService userService;

    public UserControllerV2(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List<UserDTOV2> getUsers() {
        return userService.getAllUsersV2();
    }

    @GetMapping("/{id}")
    public UserDTOV2 getUser(@PathVariable Long id) {
        return userService.getUserV2(id);
    }

    // New endpoint in V2
    @GetMapping("/{id}/profile")
    public UserProfile getUserProfile(@PathVariable Long id) {
        return userService.getUserProfile(id);
    }
}
4 files · java Explain with highlit

API versioning manages evolution while supporting existing clients. URI versioning uses paths—/api/v1/users, /api/v2/users. Header versioning employs custom headers—X-API-Version: 2. Content negotiation uses Accept headers—application/vnd.myapi.v2+json. Query parameter versioning—/api/users?version=2. I prefer URI versioning for simplicity and visibility. Versioning enables breaking changes without disrupting clients. Deprecation warnings guide migrations. Version-specific controllers or routing handle differences. Shared logic avoids duplication. Documentation clearly indicates version differences. Proper versioning strategy balances backward compatibility with forward progress. It's essential for public APIs and long-lived applications.