Creating REST Controllers Using Spring Boot
Course: Advanced Java Training
Topic: Creating REST Controllers Using Spring Boot
Course Link: Advanced Java Training Course
Demo Link: Register for a Course Demo
Website: JustAcademy Official Website
1. Introduction to REST Controllers
A REST controller is a Java class used to handle HTTP requests and return data to clients through a RESTful web service. In Spring Boot, REST controllers are commonly used to build APIs that allow frontend applications, mobile applications, and other services to communicate with backend systems.
REST stands for Representational State Transfer. It is an architectural style that uses standard HTTP methods to perform operations on resources. REST APIs commonly exchange data in JSON format, although other formats can also be supported.
Spring Boot simplifies REST API development by providing annotations, automatic configuration, HTTP message converters, and an embedded web server. Developers can create endpoints without manually implementing low-level HTTP request processing.
Why Are REST Controllers Used?
- To expose application functionality through HTTP endpoints.
- To transfer data between frontend and backend applications.
- To build APIs for web and mobile applications.
- To support communication between independent services.
- To perform CRUD operations on application resources.
- To return structured responses, commonly in JSON format.
Example Use Case
Consider an e-commerce application. A frontend application may need to retrieve product information, create a new product, update product details, or remove a product. A Spring Boot REST controller provides endpoints that handle these requests and coordinate with the application's service layer.
2. Understanding the @RestController Annotation
The @RestController annotation identifies a class as a REST-style controller in Spring MVC. It combines @Controller and @ResponseBody, so controller methods return response data directly rather than resolving a view by default.
Basic Syntax
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
// REST endpoint methods
}
Important Characteristics
@RestController makes the class eligible for Spring MVC request handling.
- Request-mapping annotations connect HTTP requests to controller methods.
- Returned Java objects can be serialized into JSON by an HTTP message converter.
- Spring manages the controller as a bean when component scanning is configured correctly.
Example
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello() {
return "Hello, Spring Boot!";
}
}
When a client sends a GET request to /hello, the controller returns the text Hello, Spring Boot!.
3. Difference Between @Controller and @RestController
| Feature |
@Controller |
@RestController |
| Primary purpose |
Often used for MVC view handling |
Often used for REST API development |
| Default return behavior |
May resolve a view name |
Writes return values to the response body by default |
| Response body |
Requires @ResponseBody when returning data directly |
Response-body behavior is included |
| Typical response |
HTML view or explicitly configured response data |
JSON, text, or other supported response data |
| Common use |
Server-rendered web pages |
Backend APIs for web and mobile clients |
4. Setting Up a Spring Boot Project
Before creating a REST controller, create a Spring Boot project and add the Spring Web dependency. Spring Web provides the Spring MVC infrastructure required for handling HTTP requests.
Required Tools
- Java Development Kit compatible with your Spring Boot version.
- Maven or Gradle for dependency management.
- IntelliJ IDEA, Eclipse, or Spring Tool Suite.
- Spring Boot and the Spring Web starter.
- Postman or curl for testing HTTP endpoints.
Creating a Project with Spring Initializr
- Open Spring Initializr.
- Select Maven or Gradle as the project build tool.
- Choose Java and a compatible Spring Boot version.
- Enter the group and artifact names.
- Add the Spring Web dependency.
- Generate and download the project.
- Import the project into your preferred IDE.
Maven Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
For a typical Spring Boot MVC application, this starter supplies the web framework and related dependencies needed to create REST endpoints.
Suggested Project Structure
src/main/java/com/example/demo/
DemoApplication.java
controller/
HelloController.java
ProductController.java
service/
ProductService.java
model/
Product.java
repository/
ProductRepository.java
src/main/resources/
application.properties
Keep controller classes in the main application's package or one of its subpackages so Spring component scanning can discover them.
5. Creating Your First REST Controller
The simplest REST controller returns a message when a client accesses a specific endpoint.
Implementation
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WelcomeController {
@GetMapping("/api/welcome")
public String welcome() {
return "Welcome to the Spring Boot REST API";
}
}
How It Works
@RestController identifies the class as a REST controller.
@GetMapping("/api/welcome") maps GET requests to the method.
- The
welcome() method returns a string.
- Spring writes the returned value into the HTTP response body.
Run the Spring Boot application and access http://localhost:8080/api/welcome. If the application uses the default port and no additional path configuration, the response will be the welcome message.
6. Understanding Request Mapping Annotations
Spring provides several annotations to map HTTP requests to Java methods. Each annotation corresponds to a common HTTP method used in REST API design.
| Annotation |
HTTP Method |
Purpose |
@GetMapping |
GET |
Retrieve a resource or collection. |
@PostMapping |
POST |
Create a resource or submit data for processing. |
@PutMapping |
PUT |
Replace a resource or perform a full update. |
@PatchMapping |
PATCH |
Partially update a resource. |
@DeleteMapping |
DELETE |
Delete a resource. |
@RequestMapping |
Multiple methods |
Define general URL and request-matching rules. |
Example with Multiple HTTP Methods
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class ExampleController {
@GetMapping("/status")
public String getStatus() {
return "Application is running";
}
@PostMapping("/submit")
public String submitData() {
return "Data submitted";
}
@PutMapping("/update")
public String updateData() {
return "Data updated";
}
@PatchMapping("/modify")
public String modifyData() {
return "Data partially modified";
}
@DeleteMapping("/remove")
public String removeData() {
return "Data removed";
}
}
The class-level @RequestMapping("/api") defines a shared URL prefix. For example, the GET endpoint is available at /api/status.
These methods illustrate request mapping. A production API should connect the endpoints to actual business operations rather than merely returning confirmation messages.
7. Using @PathVariable
The @PathVariable annotation reads a value from the URL path. It is commonly used when retrieving, updating, or deleting a particular resource using its identifier.
Example
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/{id}")
public String getProductById(@PathVariable Long id) {
return "Requested product ID: " + id;
}
}
Request: GET /api/products/101
Response: Requested product ID: 101
Explicit Path Variable Name
@GetMapping("/{productId}")
public String findProduct(
@PathVariable("productId") Long id) {
return "Product ID: " + id;
}
Using the explicit name can make the mapping clearer, especially when the URL variable and Java parameter have different names.
8. Using @RequestParam
The @RequestParam annotation extracts query parameters from a URL. It is useful for filtering, searching, sorting, and pagination.
Example
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ProductSearchController {
@GetMapping("/search")
public String searchProducts(
@RequestParam String category,
@RequestParam(defaultValue = "1") int page) {
return "Category: " + category + ", Page: " + page;
}
}
Request: GET /api/products/search?category=books&page=2
Response: Category: books, Page: 2
Use defaultValue when a parameter should have a fallback value. Use required = false when a parameter is optional and no default is needed.
9. Handling JSON Input with @RequestBody
REST APIs commonly receive request data as JSON. The @RequestBody annotation tells Spring to convert the HTTP request body into a Java object using a configured HTTP message converter.
Creating a Product Model
package com.example.demo.model;
public class Product {
private Long id;
private String name;
private double price;
public Product() {
}
public Product(Long id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
Controller Method
import com.example.demo.model.Product;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ProductCreateController {
@PostMapping
public Product createProduct(@RequestBody Product product) {
product.setId(101L);
return product;
}
}
Send a POST request to /api/products with the following JSON body and the Content-Type: application/json header:
{
"name": "Keyboard",
"price": 1499.00
}
Spring converts the JSON into a Java object and serializes the returned object into JSON. This example demonstrates request handling only; it does not save the product in a database.
10. Returning JSON Responses
A REST controller can return a Java object rather than manually building a JSON string. Spring uses a suitable HTTP message converter to serialize the object into the response format negotiated with the client.
Example
@GetMapping("/sample")
public Product getSampleProduct() {
return new Product(101L, "Keyboard", 1499.00);
}
An example JSON response is:
{
"id": 101,
"name": "Keyboard",
"price": 1499.0
}
Actual JSON property names and formatting may vary according to the model, serialization annotations, and Jackson configuration.
11. Using ResponseEntity
ResponseEntity represents an HTTP response and allows a controller to specify its status code, headers, and body. It is useful when different outcomes require different HTTP responses.
Example
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class ResponseController {
@GetMapping("/success")
public ResponseEntity<String> getSuccess() {
return ResponseEntity.ok("Request completed successfully");
}
@PostMapping("/created")
public ResponseEntity<String> createResource() {
return ResponseEntity
.status(HttpStatus.CREATED)
.body("Resource created");
}
@GetMapping("/not-found")
public ResponseEntity<String> getNotFound() {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Resource not found");
}
}
Common HTTP Status Codes
| Status Code |
Meaning |
Typical Use |
| 200 OK |
Request succeeded |
Successful retrieval or update. |
| 201 Created |
Resource created |
Successful resource creation. |
| 204 No Content |
Success without a response body |
Successful deletion. |
| 400 Bad Request |
Invalid request |
Invalid input or validation failure. |
| 401 Unauthorized |
Authentication is missing or invalid |
Authentication failure. |
| 403 Forbidden |
Access is not permitted |
Insufficient permissions. |
| 404 Not Found |
Resource not found |
Unknown resource identifier. |
| 500 Internal Server Error |
Unexpected server failure |
Unhandled server-side exception. |
12. Separating Controller and Service Layers
Controllers should focus on HTTP request handling and response creation. Business rules should generally be implemented in service classes, which can be reused by multiple controllers and tested independently.
Service Class
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
public String getGreeting(String name) {
return "Hello, " + name + "!";
}
}
Controller Class
package com.example.demo.controller;
import com.example.demo.service.GreetingService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/greetings")
public class GreetingController {
private final GreetingService greetingService;
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping
public String greet(
@RequestParam(defaultValue = "Student") String name) {
return greetingService.getGreeting(name);
}
}
The @Service annotation marks the class as a Spring-managed service component. Constructor injection allows Spring to provide the service dependency when creating the controller.
Example request: GET /api/greetings?name=Rahul
Response: Hello, Rahul!
13. Validating Request Data
Request validation helps prevent invalid data from reaching business logic. Spring Boot applications commonly use Jakarta Bean Validation annotations with the validation starter.
Required Maven Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Validated Request Model
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
public class ProductRequest {
@NotBlank(message = "Product name is required")
@Size(max = 100, message = "Name must not exceed 100 characters")
private String name;
@Positive(message = "Price must be greater than zero")
private double price;
public ProductRequest() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
Controller with Validation
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ValidatedProductController {
@PostMapping("/validate")
public ProductRequest validateProduct(
@Valid @RequestBody ProductRequest request) {
return request;
}
}
When validation fails, Spring can produce a client error response. A consistent exception-handling strategy can make validation errors easier for API consumers to understand.
14. Handling Exceptions Globally
Exceptions may occur when a resource is missing, input is invalid, or an unexpected problem occurs. Spring supports centralized error handling with @RestControllerAdvice and @ExceptionHandler.
Example
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, String>> handleBadRequest(
IllegalArgumentException ex) {
Map<String, String> error =
Map.of("message", ex.getMessage());
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(error);
}
}
This handler converts an IllegalArgumentException into an HTTP 400 response. Avoid exposing stack traces, confidential data, or internal implementation details in client-facing error messages.
15. Configuring Cross-Origin Resource Sharing (CORS)
CORS is a browser security mechanism that controls whether a web application from one origin can access resources on another origin. For example, a React frontend running on port 3000 may need to access a Spring Boot backend running on port 8080.
Example
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:3000")
public class CorsController {
@GetMapping("/data")
public String getData() {
return "Data available to the configured frontend";
}
}
This example permits requests from the specified origin for this controller. Configure allowed origins according to the application environment and security requirements.
16. Testing REST Controllers
Testing helps verify that controller endpoints return expected status codes and response bodies. Tests should cover successful requests, invalid inputs, missing resources, and other relevant conditions.
Manual Testing with Postman
- Start the Spring Boot application.
- Open Postman or another HTTP client.
- Select the required HTTP method.
- Enter the endpoint URL.
- For JSON requests, set the appropriate Content-Type header.
- Provide the request body when required.
- Send the request and inspect the status code, headers, and response body.
- Repeat with invalid inputs to verify error handling.
Automated Testing with MockMvc
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(HelloController.class)
class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnHelloMessage() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello, Spring Boot!"));
}
}
This test assumes that HelloController maps GET requests to /hello and returns the specified message. The Spring Boot test dependencies must be configured. @WebMvcTest focuses on the web layer rather than starting the full application.
17. REST Controller Best Practices
- Use resource-based URLs: Prefer paths such as
/api/products and /api/products/{id}.
- Choose appropriate HTTP methods: Use GET, POST, PUT, PATCH, and DELETE according to their intended behavior.
- Separate application layers: Keep HTTP handling in controllers and business logic in services.
- Validate incoming data: Use validation annotations and business-level checks.
- Return correct status codes: Communicate success and failure accurately.
- Use DTOs: Avoid exposing internal persistence entities when dedicated API models are more suitable.
- Handle exceptions consistently: Provide structured, useful error responses.
- Secure API endpoints: Configure authentication, authorization, and appropriate input validation.
- Document the API: Describe endpoints, request bodies, responses, and error codes.
- Test thoroughly: Cover normal operations and edge cases.
- Use pagination where appropriate: Avoid returning excessively large collections in one response.
18. Common Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
| 404 Not Found |
Incorrect URL or missing mapping |
Check endpoint paths, controller annotations, and port configuration. |
| 405 Method Not Allowed |
HTTP method does not match the mapping |
Send the method supported by the endpoint. |
| 400 Bad Request |
Invalid JSON, incompatible data type, or validation failure |
Verify the request body and validation rules. |
| 415 Unsupported Media Type |
Unsupported request content type |
Set the appropriate Content-Type header. |
| 500 Internal Server Error |
Unhandled exception or server failure |
Review server logs and correct the underlying issue. |
| Controller not detected |
Controller is outside component-scanning scope |
Place it under the main application package or configure component scanning. |
| JSON conversion failure |
Invalid JSON or incompatible Java model |
Check property types, constructors, and serialization configuration. |
| CORS error |
Frontend origin is not permitted |
Configure the allowed origin, methods, and headers. |
19. Practical Project: Product REST API
Build a Product REST API to practice creating, retrieving, updating, and deleting resources. Begin with an in-memory collection, then extend the application with a service layer, repository, and database.
Suggested Endpoints
| HTTP Method |
Endpoint |
Purpose |
| GET |
/api/products |
Retrieve all products. |
| GET |
/api/products/{id} |
Retrieve one product. |
| POST |
/api/products |
Create a product. |
| PUT |
/api/products/{id} |
Replace an existing product. |
| PATCH |
/api/products/{id} |
Update selected product fields. |
| DELETE |
/api/products/{id} |
Delete a product. |
Project Requirements
- Create a Product model with ID, name, category, and price.
- Implement the six REST endpoints.
- Use a service class for product-related business logic.
- Return HTTP 404 when a requested product does not exist.
- Validate product names and prices.
- Return suitable status codes for creation, updating, and deletion.
- Test the endpoints using Postman or automated tests.
- Optionally connect the application to a database using Spring Data JPA.
20. Interview Questions and Answers
Q1. What is a REST controller in Spring Boot?
Answer: A REST controller is a Spring-managed class that handles HTTP requests and returns response data directly, commonly as JSON. It is declared using @RestController.
Q2. What is the difference between @Controller and @RestController?
Answer: @Controller is commonly used for MVC view handling. @RestController combines @Controller and @ResponseBody, so method return values are written directly into the response body by default.
Q3. What is the purpose of @RequestMapping?
Answer: It maps requests to controller classes or methods using URL paths and optional conditions such as HTTP method, headers, and content types.
Q4. What is the difference between @PathVariable and @RequestParam?
Answer: @PathVariable reads values from the URL path, such as /products/10. @RequestParam reads query parameters, such as /products?category=books.
Q5. Why is @RequestBody used?
Answer: It instructs Spring to read the HTTP request body and convert it into a Java object using an appropriate message converter.
Q6. What is ResponseEntity?
Answer: ResponseEntity lets a controller control the HTTP status code, headers, and response body.
Q7. Why should business logic be placed in a service layer?
Answer: It separates business rules from HTTP handling, improves testability, encourages reuse, and keeps controllers easier to maintain.
Q8. How can REST API input be validated?
Answer: Use Jakarta Bean Validation annotations such as @NotBlank, @Size, and @Positive, along with @Valid and the validation dependency.
Q9. How can exceptions be handled globally?
Answer: Use @RestControllerAdvice with @ExceptionHandler methods to return consistent error responses across controllers.
Q10. How can REST controllers be tested?
Answer: Use Postman or curl for manual testing and Spring testing tools such as MockMvc for automated verification of mappings, response bodies, and HTTP status codes.
21. Practice Exercises
- Create a controller with a GET endpoint that returns a welcome message.
- Build an endpoint that accepts a user's name through a query parameter.
- Create a POST endpoint that accepts a JSON customer object.
- Use
@PathVariable to retrieve a customer by ID.
- Return HTTP 201 when a resource is created successfully.
- Validate mandatory request fields.
- Implement global exception handling for missing resources.
- Write MockMvc tests for successful and unsuccessful requests.
- Separate controller and service logic using constructor injection.
- Extend the application with Spring Data JPA and persistent storage.
22. Quick Revision Checklist
@RestController defines a REST-style controller.
@RequestMapping defines general request mappings.
@GetMapping handles GET requests.
@PostMapping handles POST requests.
@PutMapping handles PUT requests.
@PatchMapping handles PATCH requests.
@DeleteMapping handles DELETE requests.
@PathVariable reads values from URL paths.
@RequestParam reads query parameters.
@RequestBody converts request data into Java objects.
ResponseEntity controls HTTP response status, headers, and body.
@Valid triggers supported request-object validation.
@RestControllerAdvice supports centralized exception handling.
- Postman and MockMvc help test REST endpoints.
Conclusion
Creating REST controllers is a fundamental skill in Spring Boot development. Understanding request mappings, path variables, query parameters, JSON handling, HTTP status codes, validation, and exception handling helps developers build reliable REST APIs. Separating controllers from business services and testing endpoints thoroughly improves maintainability and application quality.
Learn more about backend development through the Advanced Java Training course at JustAcademy. You can also register for a course demo or visit the JustAcademy website.