CodeGym /Courses /Module 5. Spring /Creating a CRUD API using REST

Creating a CRUD API using REST

Module 5. Spring
Level 10 , Lesson 4
Available

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.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION