Data with JPACore· 50 min read

A Full CRUD API with JPA

Put it all together: create, read, update and delete real database records through a clean set of REST endpoints.

What you will learn

  • Build all four CRUD endpoints
  • Map each CRUD action to the right HTTP method
  • Handle a missing record

CRUD = the four basic actions

Almost every app does CRUD: Create, Read, Update, Delete. In a REST API each maps to an HTTP method (HTTP — HyperText Transfer Protocol — is the set of rules browsers and servers use to talk; its “methods” like GET and POST say what kind of action you want) on the same /products resource:

ActionHTTP methodURLRepository call
CreatePOST/productssave(p)
Read allGET/productsfindAll()
Read oneGET/products/{id}findById(id)
UpdatePUT/products/{id}save(p)
DeleteDELETE/products/{id}deleteById(id)

The full CRUD flow, in order

Picture using the finished API to manage one product from start to finish. Each step is one request, and each request runs one method in the controller below:

  1. Create: send POST /products with a JSON body. The controller calls save(p) and the database gives the new row an id.
  2. Read all: send GET /products. The controller calls findAll() and returns every row as a JSON array.
  3. Read one: send GET /products/1. The controller reads the {id} from the URL and calls findById(1) to return just that row.
  4. Update: send PUT /products/1 with new JSON. The controller sets the id from the URL and calls save(p), which overwrites that row.
  5. Delete: send DELETE /products/1. The controller calls deleteById(1) and the row is gone.

The full controller

A complete CRUD REST API in one controller
@RestController
@RequestMapping("/products")        // every URL below starts with /products
public class ProductController {
    private final ProductRepository repo;

    public ProductController(ProductRepository repo) {
        this.repo = repo;
    }

    @GetMapping                       // GET /products
    public List<Product> all() {
        return repo.findAll();
    }

    @GetMapping("/{id}")              // GET /products/5
    public Product one(@PathVariable Long id) {
        return repo.findById(id).orElseThrow();
    }

    @PostMapping                      // POST /products
    public Product create(@RequestBody Product p) {
        return repo.save(p);
    }

    @PutMapping("/{id}")              // PUT /products/5
    public Product update(@PathVariable Long id, @RequestBody Product p) {
        p.setId(id);                  // make sure we update the right row
        return repo.save(p);
    }

    @DeleteMapping("/{id}")           // DELETE /products/5
    public String delete(@PathVariable Long id) {
        repo.deleteById(id);
        return "Deleted product " + id;
    }
}

Note: Output: POST /products {"name":"Keyboard","price":799} -> {"id":1,"name":"Keyboard","price":799.0} GET /products -> [{"id":1,"name":"Keyboard","price":799.0}] PUT /products/1 {"name":"Keyboard Pro","price":999} -> {"id":1,"name":"Keyboard Pro","price":999.0} DELETE /products/1 -> Deleted product 1 Five endpoints give complete control over the data — all backed by the free repository methods.

One nice helper

Notice @RequestMapping("/products") on the class. It sets a base path so every method’s URL starts with /products — you do not repeat it on each method.

Also notice findById(id).orElseThrow(). findById returns an Optional (it might be empty if the id does not exist). orElseThrow() turns an empty result into an error instead of a confusing null. Cleaner error handling comes in the next unit.

Tip: PUT vs POST: POST creates a new item (no id yet); PUT updates an existing item at a known id. Following these conventions makes your API predictable for anyone using it.

Watch out: In update, setting the id from the path (p.setId(id)) is important. Without it, save could create a brand-new row instead of updating the one you meant.

Q. In a REST API, which HTTP method is normally used to delete a record?

Answer: DELETE removes a resource, usually at /things/{id}. GET reads, POST creates, and PUT updates an existing record.

✍️ Practice

  1. Build the full five-endpoint CRUD API for a Book entity.
  2. Test create, read all, read one, update and delete with Postman or curl.

🏠 Homework

  1. Build complete CRUD for a Task entity (id, title, done) and test every endpoint.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →