Skip to content

Controllers

Spring Boot controllers are thin HTTP adapters. Map routes, delegate to services, return ResponseEntity. No business logic here.

Pattern

@RestController
@RequestMapping("/player")
@Profile("api")
public class PlayerController {

    private static final Logger log = LoggerFactory.getLogger(PlayerController.class);

    private final PlayerService playerService;

    @Autowired
    public PlayerController(PlayerService playerService) {
        this.playerService = playerService;
    }

    @GetMapping
    public ResponseEntity<List<Player>> findAll() {
        return ResponseEntity.ok(playerService.findAll());
    }

    @GetMapping("/{id}")
    public ResponseEntity<Player> findById(@PathVariable("id") Long id) {
        log.debug("Finding player by id: {}", id);
        return playerService.findById(id)
                .map(ResponseEntity::ok)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "Player not found with id: " + id
                ));
    }

    @PostMapping
    public ResponseEntity<Player> create(@RequestBody Player player) {
        return ResponseEntity.ok(playerService.save(player));
    }

    @PutMapping("/{id}")
    public ResponseEntity<Player> update(@PathVariable("id") Long id, @RequestBody Player player) {
        player.setId(id);
        return ResponseEntity.ok(playerService.save(player));
    }

    @PatchMapping("/{id}")
    public ResponseEntity<Player> patchPlayer(Long id, Map<String, Object> updates) {
        Player existingPlayer = playerService.findById(id)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "Player not found with id: " + id
                ));
        return ResponseEntity.ok(playerService.patch(existingPlayer, updates));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable("id") Long id) {
        playerService.deleteById(id);
        return ResponseEntity.ok(null);
    }
}

Notes from the ESPN project this came from:

  • Constructor injection over field injection.
  • Optional.map(ResponseEntity::ok).orElseThrow(ResponseStatusException) is the clean 404 pattern.
  • PATCH takes a Map<String, Object> of partial updates — the service applies them, not the controller.
  • @Profile("api") keeps the controller off non-API profiles.

See also