Java Spring Boot REST API Development Guide: Controllers, Data Access and Security

Spring Boot helps you create stand-alone, production-grade Spring applications you can just run. It is opinionated out of the box but gets out of the way as requirements diverge: embedded servers, auto-configuration, and externalized configuration come built in, with no XML. Based on the official Spring guide, this article follows four threads: starters, controllers, data access, and security.

Starting with Spring Initializr

Visit start.spring.io to generate a project: choose Java 17+ and Gradle or Maven, then pick the dependencies (starters) you need. Key starters include:

  • spring-boot-starter-web: embedded Tomcat plus Spring MVC for REST APIs;
  • spring-boot-starter-data-jpa: JPA data access;
  • spring-boot-starter-security: authentication and authorization.

The main class carries @SpringBootApplication, which combines @Configuration, @EnableAutoConfiguration, and @ComponentScan. Launch with ./mvnw spring-boot:run or java -jar — no XML anywhere.

Writing REST Controllers

Spring handles HTTP requests with controllers. @RestController means every method's returned domain object is serialized directly to JSON, and annotations like @GetMapping / @PostMapping map HTTP verbs to methods:

@RestController
public class GreetingController {

  @GetMapping("/greeting")
  public Greeting greeting(@RequestParam(defaultValue = "World") String name) {
    return new Greeting(counter.incrementAndGet(), "Hello, " + name + "!");
  }
}

With Jackson on the classpath, returned objects are automatically serialized to JSON — no manual conversion code.

The Data Access Layer

The most common approach is Spring Data JPA: define an interface extending JpaRepository, and CRUD, pagination, and sorting become available automatically. Complex queries are expressed via method-name derivation or @Query:

public interface FlightRepository extends JpaRepository<Flight, Long> {
  List<Flight> findByActiveTrue();
}

Key datasource settings live in application.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/demo
    username: demo
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate

Putting the password in an environment variable instead of hardcoding it is a habit worth building before launch.

For data access, Spring Data JPA suits projects with complex domain models that want fast CRUD. If your team prefers hand-written SQL or demands fine-grained query control, swap in spring-boot-starter-jdbc with JdbcTemplate or use MyBatis instead. Both routes coexist happily with Spring Boot's auto-configuration — just avoid mixing two persistence styles inside the same service.

Common Annotations and Best Practices

Beyond @RestController, a few annotations are worth knowing:

Annotation Purpose
@PathVariable Reads a value from the URL path, e.g. /users/{id}
@RequestBody Deserializes a JSON request body into an object
@ResponseStatus Sets a success status code, e.g. 201
@Valid / @Validated Triggers Jakarta Bean Validation
@ExceptionHandler Centralizes exception handling into structured errors

Keep controllers thin: they receive parameters and assemble responses, business logic lives in a Service layer, and data access goes to a Repository. That clean layering makes testing and swapping implementations much easier.

Security Configuration

Adding spring-boot-starter-security secures every endpoint by default. A dependency-injected SecurityFilterChain bean lets you control exactly which paths are public, which require login, and which authentication scheme to use (Basic, JWT, OAuth2). A typical setup that restricts /api/** to authenticated users:

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeHttpRequests(auth -> auth
      .requestMatchers("/public/**", "/actuator/health").permitAll()
      .requestMatchers("/api/**").authenticated())
    .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
    .build();
}

With spring-security-oauth2-resource-server, declaring the JWT issuer is enough — token validation and signature verification are handled automatically.

A Complete Scenario: The Flight Query API

Assembling the pieces, a "list active flights" endpoint is layered roughly like this: FlightController receives GET /flights?active=true and passes the active parameter to FlightService; the service applies business logic (grouping by destination, dropping canceled flights) and calls FlightRepository.findByActiveTrue(); the result is wrapped in a shared ApiResponse that returns success, data, and a timestamp. The controller only receives parameters and assembles responses, so changing the data structure touches at most the controller and DTOs — never the database layer.

Testing the endpoint is equally straightforward: @WebMvcTest loads only the controller slice and asserts JSON with MockMvc; @DataJpaTest validates repository queries against an in-memory database; and a full @SpringBootTest brings up the complete context for integration tests. The three layers each have their job, and all run in seconds.

Packaging and Deployment

Spring Boot builds an executable JAR containing all dependencies, classes, and resources, making it easy to ship, version, and deploy across environments:

./gradlew bootRun        # run locally
./gradlew build          # build the JAR
java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

The embedded server turns deployment from "install Tomcat, configure web.xml" into "run one command", and the same artifact is trivially containerized with Docker.

Monitoring and Operations

Spring Boot Actuator exposes health checks, metrics, and log endpoints: after adding spring-boot-starter-actuator, /actuator/health can drive load balancers and container liveness probes, and /actuator/metrics plugs into Prometheus and similar systems. Log through the SLF4J facade as structured JSON and push it to a centralized log platform — on-call debugging gets much faster. For tests, spring-boot-starter-test bundles JUnit and MockMvc for controller and service unit and integration tests, and the Initializr template ships a sample test so ./gradlew test gives you a passing baseline out of the box. Where horizontal scaling matters, Spring Boot's lightweight, standardized packaging deploys easily onto managed container services from the major cloud vendors.

Frequently Asked Questions

  • Why are all endpoints secured by default? It is a safe default: every exposed endpoint is a larger attack surface. Tighten first, then open up as needed — safer than opening up and patching later.
  • Port already in use at startup? Override the server.port property, or let the deployment platform inject an environment variable, instead of editing code.
  • Is JPA auto-DDL production-ready? ddl-auto: create is for development only; in production use validate, or hand migrations to Flyway or Liquibase.

16IDC perspective

Spring Boot suits mid-to-large teams and projects that value stability and ecosystem maturity, with the deepest support across enterprise middleware and cloud vendors. Before choosing, read Website API Integration Basics, and put API Security with OAuth 2.0 and JWT into practice before launch. More backend content lives in the Backend Integration category.

Source: https://spring.io/guides/gs/rest-service/
Reference: Spring Boot documentation https://docs.spring.io/spring-boot/index.html
Reference: Spring Security documentation https://docs.spring.io/spring-security/reference/index.html