Backend Development 15 min read

Top 10 Common Spring Boot REST API Mistakes and How to Fix Them

This article examines the ten most frequent errors developers make when building Spring Boot REST APIs, explains the negative impact of each mistake, and offers concrete, up‑to‑date solutions with code examples, while also highlighting a continuously updated collection of over 70 Spring Boot 3 practical case studies.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Top 10 Common Spring Boot REST API Mistakes and How to Fix Them

Environment: SpringBoot 3.2.5

1. Introduction

Building robust and efficient REST APIs is essential for modern application development. Although Spring Boot simplifies this process, developers often make mistakes that can lead to inefficiency, security vulnerabilities, or poor user experience. This article explores the top ten common mistakes in Spring Boot REST APIs, explains their impact, and provides updated solutions using the latest Spring Boot features and best practices.

2. Detailed Errors

2.1 Using Wrong HTTP Status Codes

Error: Returning 200 OK for all responses, including error cases.

Impact: Misleading status codes confuse API clients and make debugging difficult.

Solution: Always use appropriate HTTP status codes based on the situation: 200 OK – request succeeded. 201 Created – resource successfully created. 400 Bad Request – invalid input or validation error. 404 Not Found – resource does not exist. 500 Internal Server Error – unexpected server error.

<code>@RestController
@RequestMapping("/api/products")
public class ProductController {
  @GetMapping("/{id}")
  public ResponseEntity<Product> getProductById(@PathVariable Long id) {
    return productService.findById(id)
      .map(product -> ResponseEntity.ok(product))
      .orElse(ResponseEntity.status(HttpStatus.NOT_FOUND).build());
  }
}
</code>

Why it matters: Correct HTTP status codes improve client understanding and API reliability.

2.2 Not Validating Input Data

Error: Accepting data without validation.

Impact: Leads to security vulnerabilities and subsequent errors.

Solution: Use @Valid on DTOs for input validation and handle errors gracefully.

<code>@RestController
@RequestMapping("/api/products")
public class ProductController {
  @PostMapping
  public ResponseEntity<String> createProduct(@Valid @RequestBody ProductDTO productDTO) {
    productService.save(productDTO);
    return ResponseEntity.status(HttpStatus.CREATED).body("success");
  }
}

public record ProductDTO(@NotNull(message = "名称不能为空") String name,
                         @Positive(message = "价格必须大于0") Double price) {}
</code>

Why it matters: Validation ensures data integrity and prevents abuse.

2.3 Ignoring API Versioning

Error: Developing APIs without version control, causing breaking changes for clients.

Impact: Compatibility issues as the API evolves.

Solution: Implement versioning via URI or custom headers.

<code>@RestController
@RequestMapping("/api/v1/products")
public class ProductV1Controller {
  @GetMapping("/{id}")
  public Product getProductV1(@PathVariable Long id) {
    return productService.findByIdV1(id);
  }
}

@RestController
@RequestMapping("/api/v2/products")
public class ProductV2Controller {
  @GetMapping("/{id}")
  public ProductDTO getProductV2(@PathVariable Long id) {
    return productService.findByIdV2(id);
  }
}
</code>

Why it matters: Versioning preserves backward compatibility while adding new features.

2.4 Hard‑coding Endpoints and Configuration

Error: Hard‑coding URLs or service endpoints in code.

Impact: Makes the application hard to maintain and configure.

Solution: Externalize configuration using application.yml or application.properties .

<code># application.yml
pack:
  product:
    service:
      url: https://api.pack.com/products
</code>
<code>@Value("${pack.product.service.url}")
private String productServiceUrl;
</code>

Why it matters: Externalized configuration makes the application more flexible and maintainable.

2.5 Improper Exception Handling

Error: Allowing exceptions to propagate to clients without proper formatting.

Impact: Clients receive unclear error messages, causing confusion.

Solution: Use @ExceptionHandler within a @ControllerAdvice for centralized error handling.

<code>@RestControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(ResourceNotFoundException.class)
  public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
  }

  @ExceptionHandler(Exception.class)
  public ResponseEntity<String> handleGenericException(Exception ex) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("发生错误: " + ex.getMessage());
  }
}
</code>

Why it matters: Proper exception handling improves client experience and debugging efficiency.

2.6 Exposing JPA Entities Directly

Error: Returning database entities directly in API responses.

Impact: Tight coupling between database schema and API.

Solution: Use DTOs to decouple API responses from the database model.

<code>public record ProductDTO(Long id, String name, Double price) {}

public ProductDTO mapToDTO(Product product) {
  return new ProductDTO(product.getId(), product.getName(), product.getPrice());
}
</code>

Why it matters: DTOs increase API flexibility and prevent sensitive data leakage.

2.7 Missing Pagination and Filtering

Error: Returning large data sets in a single response.

Impact: Performance bottlenecks and client‑side issues.

Solution: Implement pagination and filtering with Pageable .

<code>@GetMapping
public Page<Product> getAllProducts(Pageable pageable) {
  return productRepository.findAll(pageable);
}
</code>

Why it matters: Pagination and filtering improve API performance and scalability.

2.8 Ignoring API Security

Error: Not protecting the REST API or exposing sensitive data.

Impact: Unauthorized access and potential data leaks.

Solution: Secure the API with Spring Security using JWT or OAuth2.

<code>@Bean
SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Throwable {
  http.csrf(csrf -> csrf.disable());
  http.authorizeHttpRequests(registry -> {
    registry.requestMatchers("/*/*.js", "/*/*.css", "*.css", "*.js", "*.html", "/*/*.html", "/login", "/logout").permitAll();
    registry.requestMatchers("/**").authenticated();
  });
  http.securityMatcher("/api/**", "/admin/**", "/login", "/logout", "/default-ui.css");
  http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
  return http.build();
}
</code>

Why it matters: Security protects sensitive data and ensures compliance.

2.9 Skipping API Documentation

Error: Not writing API documentation.

Impact: Other developers find it hard to use the API.

Solution: Use Knife4j (or Swagger) to generate documentation automatically.

<code>&lt;dependency&gt;
  &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
  &lt;artifactId&gt;knife4j-spring-boot-starter&lt;/artifactId&gt;
&lt;/dependency&gt;
</code>

Why it matters: Documentation boosts developer productivity and collaboration.

2.10 Ignoring HATEOAS

Error: Returning plain JSON without navigation links.

Impact: Clients lack guidance for related actions.

Solution: Use Spring HATEOAS to include navigation links.

<code>import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@RestController
@RequestMapping("/products")
public class ProductController {
  @GetMapping("/{id}")
  public EntityModel<ProductDTO> queryProductById(@PathVariable Long id) {
    ProductDTO productDTO = new ProductDTO(id, "鼠标", 66.6D);
    EntityModel<ProductDTO> productModel = EntityModel.of(
      productDTO,
      linkTo(methodOn(ProductController.class).queryProductById(productDTO.id())).withSelfRel(),
      linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products")
    );
    return productModel;
  }

  @GetMapping("")
  public List<EntityModel<ProductDTO>> queryProducts() {
    return List.of(
      EntityModel.of(new ProductDTO(1L, "鼠标", 66.6),
        linkTo(methodOn(ProductController.class).queryProductById(1L)).withSelfRel(),
        linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products")),
      EntityModel.of(new ProductDTO(2L, "键盘", 88.8),
        linkTo(methodOn(ProductController.class).queryProductById(2L)).withSelfRel(),
        linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products"))
    );
  }
}
</code>

Accessing /products/666 yields the following responses:

These examples demonstrate how proper status codes, validation, versioning, externalized configuration, centralized exception handling, DTO usage, pagination, security, documentation, and HATEOAS can greatly improve the quality, maintainability, and usability of Spring Boot REST APIs.

backendJavaSpring BootREST APICommon Mistakes
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.