<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>SHEN LI (EN)</title><description>English posts — agents, LLM internals, and engineering notes.</description><link>https://shenli.online/en</link><language>en-us</language><item><title>Best Practices for Building RESTful APIs with Spring Boot</title><link>https://shenli.online/en/blog/20260615-spring-boot-api-practices/post</link><guid isPermaLink="true">https://shenli.online/en/blog/20260615-spring-boot-api-practices/post</guid><description>Lessons from real projects on designing Spring Boot RESTful APIs: unified response envelopes, global exception handling, validation, and API versioning.</description><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Over the past year I have worked on several Spring Boot backend projects and gradually built up a set of opinions about RESTful API design. This post collects the practices I consider most important.&lt;/p&gt;
&lt;h2&gt;1. A unified response envelope&lt;/h2&gt;
&lt;p&gt;A consistent response shape lowers communication cost during both development and debugging. I usually go with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;code&quot;: 200,
  &quot;message&quot;: &quot;success&quot;,
  &quot;data&quot;: {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Java side:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Data
public class ApiResponse&amp;#x3C;T&gt; {
    private Integer code;
    private String message;
    private T data;

    public static &amp;#x3C;T&gt; ApiResponse&amp;#x3C;T&gt; success(T data) {
        ApiResponse&amp;#x3C;T&gt; response = new ApiResponse&amp;#x3C;&gt;();
        response.setCode(200);
        response.setMessage(&quot;success&quot;);
        response.setData(data);
        return response;
    }

    public static &amp;#x3C;T&gt; ApiResponse&amp;#x3C;T&gt; error(Integer code, String message) {
        ApiResponse&amp;#x3C;T&gt; response = new ApiResponse&amp;#x3C;&gt;();
        response.setCode(code);
        response.setMessage(message);
        return response;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Global exception handling&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;@RestControllerAdvice&lt;/code&gt; and &lt;code&gt;@ExceptionHandler&lt;/code&gt;, exception handling can be centralised cleanly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse&amp;#x3C;?&gt; handleValidation(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getFieldErrors()
            .stream()
            .map(e -&gt; e.getField() + &quot;: &quot; + e.getDefaultMessage())
            .collect(Collectors.joining(&quot;, &quot;));
        return ApiResponse.error(400, msg);
    }

    @ExceptionHandler(Exception.class)
    public ApiResponse&amp;#x3C;?&gt; handleException(Exception ex) {
        return ApiResponse.error(500, &quot;Internal server error&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Request validation&lt;/h2&gt;
&lt;p&gt;Spring Boot ships with Bean Validation; combined with &lt;code&gt;@Valid&lt;/code&gt; it makes request validation straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    @PostMapping
    public ApiResponse&amp;#x3C;User&gt; create(@Valid @RequestBody CreateUserRequest request) {
        // business logic
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. API versioning&lt;/h2&gt;
&lt;p&gt;Keeping the version in the URL path is the option I recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/api/v1/users&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/api/v2/users&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That makes a breaking upgrade a gradual transition rather than a flag day.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;These are the practices I rely on when building RESTful APIs with Spring Boot. Good API design should be settled at the start of a project to avoid rework later. Hope it helps.&lt;/p&gt;</content:encoded></item></channel></rss>