CRUD — an acronym for four operations: Create (Creation), Read (Reading), Update (Updating) and Delete (Deletion). In the context of a REST API, CRUD maps to the following set of HTTP methods:
| Operation (CRUD) | HTTP Method | Purpose |
|---|---|---|
| Create | POST | Creating a new resource |
| Read | GET | Retrieving resource data |
| Update | PUT/PATCH | Modifying an existing resource |
| Delete | DELETE | Removing a resource |
Building a CRUD API is the foundation for interacting with any resources in a web app. Let's build our first full REST API to manage, say, the Product entity.
Building entities and repositories
Creating the Product entity
To work with database data in Spring we use JPA annotations (Java Persistence API). Let's create a Product class that represents our entity.
package com.example.demo.model;
import jakarta.persistence.*;
// Annotation @Entity makes the class a JPA entity
@Entity
@Table(name = "products") // Table name in the database
public class Product {
@Id // Primary key
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-increment
private Long id;
@Column(nullable = false) // Field must not be null
private String name;
private String description;
@Column(nullable = false)
private Double price;
// Getters and setters (for working with fields via Hibernate)
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 String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Creating the repository
To interact with the database we use Spring Data JPA interfaces like JpaRepository. Let's create a repository for the Product entity.
package com.example.demo.repository;
import com.example.demo.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// Repository for working with Product
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
With this repository we'll perform all basic DB operations: saving, fetching, deleting, and updating records.
Implementing CRUD controllers
Now let's create a REST controller that performs the CRUD operations.
Creating the CRUD controller
package com.example.demo.controller;
import com.example.demo.model.Product;
import com.example.demo.repository.ProductRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// The @RestController annotation indicates that this is a RESTful controller
@RestController
@RequestMapping("/api/products") // Base route for all requests
public class ProductController {
private final ProductRepository productRepository;
// Dependency injection via constructor
public ProductController(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// Read all products (Read - GET)
@GetMapping
public List<Product> getAllProducts() {
return productRepository.findAll();
}
// Read a product by ID (Read - GET)
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productRepository.findById(id)
.map(product -> ResponseEntity.ok(product))
.orElse(ResponseEntity.notFound().build());
}
// Create a new product (Create - POST)
@PostMapping
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
Product savedProduct = productRepository.save(product);
return ResponseEntity.status(HttpStatus.CREATED).body(savedProduct);
}
// Update an existing product (Update - PUT)
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable Long id, @RequestBody Product productDetails) {
return productRepository.findById(id)
.map(existingProduct -> {
existingProduct.setName(productDetails.getName());
existingProduct.setDescription(productDetails.getDescription());
existingProduct.setPrice(productDetails.getPrice());
Product updatedProduct = productRepository.save(existingProduct);
return ResponseEntity.ok(updatedProduct);
})
.orElse(ResponseEntity.notFound().build());
}
// Delete a product (Delete - DELETE)
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
return productRepository.findById(id)
.map(product -> {
productRepository.delete(product);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
The REST API for CRUD operations is ready. Let's add a bit of hands-on practice.
Hands-on exercise: testing the API
Start the application and make sure it runs. Here are some example requests you can make using Postman or curl.
Create product (POST)
Request:
POST /api/products
Content-Type: application/json
{
"name": "Laptop",
"description": "A powerful gaming laptop",
"price": 1500.0
}
Response:
{
"id": 1,
"name": "Laptop",
"description": "A powerful gaming laptop",
"price": 1500.0
}
Get all products (GET)
Request:
GET /api/products
Response:
[
{
"id": 1,
"name": "Laptop",
"description": "A powerful gaming laptop",
"price": 1500.0
}
]
Update product (PUT)
Request:
PUT /api/products/1
Content-Type: application/json
{
"name": "Laptop Pro",
"description": "An even more powerful gaming laptop",
"price": 2000.0
}
Response:
{
"id": 1,
"name": "Laptop Pro",
"description": "An even more powerful gaming laptop",
"price": 2000.0
}
Delete product (DELETE)
Request:
DELETE /api/products/1
Response:
204 No Content
Common errors and how to fix them
Error: Resource Not Found (404)
If you pass an incorrect resource ID, the app will return 404 Not Found. That's expected behavior.
Tip: always check the ID before making a request to avoid unnecessary handling.
Error: Invalid JSON Body
If the request body doesn't match the expected format, you'll get a 400 Bad Request error.
Tip: make sure your JSON is well-formed, and use the built-in validators in Postman or your IDE.
How this is used in real projects
Building REST APIs is the backbone of most modern web apps. Typical scenarios:
- Managing a product catalog for an e-commerce store.
- Serving data to mobile apps.
- Communication between microservices via REST API.
Your CRUD APIs are the basic building block of any backend project. So hone your skill until it's sharp — your app's success often depends on it.
GO TO FULL VERSION