Spring REST API
Build powerful RESTful web services with Spring
🌐 What is Spring REST API?
Spring REST API enables building RESTful web services using Spring MVC. It provides annotations and tools for creating HTTP endpoints, handling requests/responses, and implementing REST architectural principles effortlessly.
// Simple REST controller
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping
public List<Product> getAllProducts() {
return productService.findAll();
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
}
}
REST API Features
HTTP Methods
Support for GET, POST, PUT, DELETE operations
@GetMapping("/users/{id}")
@PostMapping("/users")
JSON Serialization
Automatic JSON conversion for requests/responses
@RequestBody User user
@ResponseBody
Path Variables
Extract values from URL paths
@PathVariable Long id
Exception Handling
Global error handling and custom responses
@ExceptionHandler
@ControllerAdvice
🔹 Complete REST Controller
Build a full CRUD REST API:
@RestController
@RequestMapping("/api/books")
@CrossOrigin(origins = "http://localhost:3000")
public class BookController {
@Autowired
private BookService bookService;
// GET /api/books - Get all books
@GetMapping
public ResponseEntity<List<Book>> getAllBooks() {
List<Book> books = bookService.findAll();
return ResponseEntity.ok(books);
}
// GET /api/books/{id} - Get book by ID
@GetMapping("/{id}")
public ResponseEntity<Book> getBookById(@PathVariable Long id) {
Book book = bookService.findById(id);
if (book != null) {
return ResponseEntity.ok(book);
}
return ResponseEntity.notFound().build();
}
// POST /api/books - Create new book
@PostMapping
public ResponseEntity<Book> createBook(@Valid @RequestBody Book book) {
Book savedBook = bookService.save(book);
return ResponseEntity.status(HttpStatus.CREATED).body(savedBook);
}
// PUT /api/books/{id} - Update book
@PutMapping("/{id}")
public ResponseEntity<Book> updateBook(
@PathVariable Long id,
@Valid @RequestBody Book book) {
Book updatedBook = bookService.update(id, book);
if (updatedBook != null) {
return ResponseEntity.ok(updatedBook);
}
return ResponseEntity.notFound().build();
}
// DELETE /api/books/{id} - Delete book
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
boolean deleted = bookService.deleteById(id);
if (deleted) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.notFound().build();
}
// GET /api/books/search - Search books
@GetMapping("/search")
public ResponseEntity<List<Book>> searchBooks(
@RequestParam(required = false) String title,
@RequestParam(required = false) String author) {
List<Book> books = bookService.search(title, author);
return ResponseEntity.ok(books);
}
}
API Endpoints:
GET /api/books → List all books
POST /api/books → Create book
PUT /api/books/1 → Update book
DELETE /api/books/1 → Delete book
🔹 Request/Response Handling
Handle different types of HTTP requests and responses:
@RestController
@RequestMapping("/api/users")
public class UserController {
// Request Parameters
@GetMapping
public List<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String search) {
return userService.findUsers(page, size, search);
}
// Path Variables
@GetMapping("/{userId}/orders/{orderId}")
public Order getUserOrder(
@PathVariable Long userId,
@PathVariable Long orderId) {
return orderService.findByUserAndId(userId, orderId);
}
// Request Headers
@PostMapping
public ResponseEntity<User> createUser(
@RequestBody User user,
@RequestHeader("Authorization") String authHeader) {
User savedUser = userService.create(user);
HttpHeaders headers = new HttpHeaders();
headers.add("Location", "/api/users/" + savedUser.getId());
return ResponseEntity.status(HttpStatus.CREATED)
.headers(headers)
.body(savedUser);
}
// File Upload
@PostMapping("/{id}/avatar")
public ResponseEntity<String> uploadAvatar(
@PathVariable Long id,
@RequestParam("file") MultipartFile file) {
try {
String filename = userService.saveAvatar(id, file);
return ResponseEntity.ok("Avatar uploaded: " + filename);
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Upload failed");
}
}
}
🔹 Data Validation
Validate request data using Bean Validation:
// Entity with validation annotations
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Username is required")
@Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
private String username;
@Email(message = "Email should be valid")
@NotBlank(message = "Email is required")
private String email;
@NotBlank(message = "Password is required")
@Size(min = 6, message = "Password must be at least 6 characters")
private String password;
@Min(value = 18, message = "Age must be at least 18")
@Max(value = 100, message = "Age must be less than 100")
private Integer age;
// Getters and setters
}
// Controller with validation
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<?> createUser(@Valid @RequestBody User user,
BindingResult result) {
if (result.hasErrors()) {
Map<String, String> errors = new HashMap<>();
result.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
Validation Error Response:
400 Bad Request
{"username": "Username is required", "email": "Email should be valid"}
🔹 Global Exception Handling
Handle exceptions globally across all controllers:
// Custom exceptions
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
// Global exception handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> handleBadRequest(
BadRequestException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
ex.getMessage(),
System.currentTimeMillis()
);
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"An unexpected error occurred",
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
// Error response class
public class ErrorResponse {
private int status;
private String message;
private long timestamp;
// Constructors, getters, setters
}
🔹 API Documentation with OpenAPI
Document your REST API automatically:
// Add dependency: springdoc-openapi-starter-webmvc-ui
@RestController
@RequestMapping("/api/products")
@Tag(name = "Product", description = "Product management APIs")
public class ProductController {
@Operation(summary = "Get all products",
description = "Retrieve a list of all products")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved products"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping
public List<Product> getAllProducts() {
return productService.findAll();
}
@Operation(summary = "Create a new product")
@PostMapping
public ResponseEntity<Product> createProduct(
@Parameter(description = "Product to be created")
@RequestBody Product product) {
Product savedProduct = productService.save(product);
return ResponseEntity.status(HttpStatus.CREATED).body(savedProduct);
}
}
// Entity with schema documentation
@Entity
@Schema(description = "Product entity")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Unique identifier of the product")
private Long id;
@Schema(description = "Name of the product", example = "iPhone 13")
private String name;
@Schema(description = "Price of the product", example = "999.99")
private BigDecimal price;
}
Swagger UI:
Access API documentation at: http://localhost:8080/swagger-ui.html