Spring REST Getting Started
This tutorial gets you from zero to a running REST endpoint. Add one dependency to your pom.xml, write one YAML file, and your endpoint is live. No controllers, no @RequestMapping, no Spring MVC configuration.
Maven dependency
The starter is published to Maven Central, so no additional repository configuration is needed. Adding a single dependency to pom.xml is all that is required:
<dependency>
<groupId>net.officefloor.springboot</groupId>
<artifactId>officefloor-rest-spring-boot-4-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Application class
The application entry point is a standard Spring Boot class with no OfficeFloor-specific code:
@SpringBootApplication
public class SpringRestGettingStartedApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRestGettingStartedApplication.class, args);
}
}
Your first endpoint
An endpoint is a YAML file placed under src/main/resources/officefloor/rest/. The file name encodes the HTTP method and the URL path:
officefloor/rest/
└── greeting.GET.yml → GET /greeting
The YAML file names the Java class that handles the request:
service:
class: net.officefloor.tutorial.springrestgettingstarted.GetGreetingLogic
The handler is a plain Java class with no framework annotations on the class itself:
public class GetGreetingLogic {
public void service(GreetingService greetingService, ObjectResponse<GreetingResponse> response) {
response.send(new GreetingResponse(greetingService.greet("World")));
}
}
OfficeFloor registers every Spring bean in the application context as a managed object. GreetingService is a plain Spring @Service injected automatically by type into any service method parameter whose type matches:
@Service
public class GreetingService {
public String greet(String name) {
return "Hello, " + name + "!";
}
}
ObjectResponse<T> serialises the object to JSON and writes it to the HTTP response. No @ResponseBody or @RestController is needed.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GreetingResponse {
private String message;
}
Path parameter
A path variable in the URL is expressed by a curly-brace file or directory name:
officefloor/rest/
└── greeting/
└── {name}.GET.yml → GET /greeting/{name}
service:
class: net.officefloor.tutorial.springrestgettingstarted.GetNamedGreetingLogic
The handler receives the path variable as a @PathVariable parameter. Always use the name = attribute form. The shorthand @PathVariable("name") sets the value attribute; OfficeFloor resolves arguments from raw Java reflection where @AliasFor synthesis is not applied, so the shorthand silently produces an empty name and the binding fails:
public class GetNamedGreetingLogic {
public void service(
@PathVariable(name = "name") String name,
GreetingService greetingService,
ObjectResponse<GreetingResponse> response) {
response.send(new GreetingResponse(greetingService.greet(name)));
}
}
Testing
The application is a standard Spring Boot application, so tests use MockMvc:
@SpringBootTest
@AutoConfigureMockMvc
public class SpringRestGettingStartedTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper mapper;
@Test
public void getGreeting() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/greeting")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapper.writeValueAsString(new GreetingResponse("Hello, World!"))));
}
@Test
public void getNamedGreeting() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/greeting/OfficeFloor")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapper.writeValueAsString(new GreetingResponse("Hello, OfficeFloor!"))));
}
}
Running against a real server
Because the application is standard Spring Boot, it can be run directly:
mvn spring-boot:run
With the server running, the endpoints respond to plain HTTP:
curl http://localhost:8080/greeting
{"message":"Hello, World!"}
curl http://localhost:8080/greeting/OfficeFloor
{"message":"Hello, OfficeFloor!"}
The integration test does the same thing programmatically. @SpringBootTest(webEnvironment = RANDOM_PORT) starts an embedded server on a random port and TestRestTemplate makes real HTTP calls:
@AutoConfigureTestRestTemplate
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringRestGettingStartedRealServerTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void getGreeting() {
ResponseEntity<GreetingResponse> response = restTemplate.getForEntity(
"/greeting", GreetingResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(new GreetingResponse("Hello, World!"), response.getBody());
}
@Test
public void getNamedGreeting() {
ResponseEntity<GreetingResponse> response = restTemplate.getForEntity(
"/greeting/OfficeFloor", GreetingResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(new GreetingResponse("Hello, OfficeFloor!"), response.getBody());
}
}
Next
The Spring REST tutorial covers YAML naming conventions in depth, multi-method service classes, multiple path variables, and custom response headers via ResponseEntity.

