Building a REST API with Spring Boot

2 minute read

This chapter builds a complete CRUD REST API for our Bookstore. You’ll learn @RestController, HTTP method mapping, JSON serialization, and how to test endpoints.


REST Basics

HTTP Method Operation Example
GET Read GET /api/books
POST Create POST /api/books
PUT Full update PUT /api/books/1
PATCH Partial update PATCH /api/books/1
DELETE Delete DELETE /api/books/1

The Entity

@Entity
@Table(name = "books")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String author;

    @Column(unique = true)
    private String isbn;

    private BigDecimal price;

    private LocalDate publishedDate;

    // Constructors, getters, setters
    public Book() {}

    public Book(String title, String author, String isbn, BigDecimal price) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
        this.price = price;
    }

    // getters and setters...
}

The Repository

public interface BookRepository extends JpaRepository<Book, Long> {
}

The Service

@Service
public class BookService {

    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public List<Book> findAll() {
        return bookRepository.findAll();
    }

    public Book findById(Long id) {
        return bookRepository.findById(id)
            .orElseThrow(() -> new ResponseStatusException(
                HttpStatus.NOT_FOUND, "Book not found with id: " + id));
    }

    public Book create(Book book) {
        return bookRepository.save(book);
    }

    public Book update(Long id, Book updated) {
        Book book = findById(id);
        book.setTitle(updated.getTitle());
        book.setAuthor(updated.getAuthor());
        book.setIsbn(updated.getIsbn());
        book.setPrice(updated.getPrice());
        return bookRepository.save(book);
    }

    public void delete(Long id) {
        Book book = findById(id);
        bookRepository.delete(book);
    }
}

The Controller

@RestController
@RequestMapping("/api/books")
public class BookController {

    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping
    public List<Book> getAll() {
        return bookService.findAll();
    }

    @GetMapping("/{id}")
    public Book getById(@PathVariable Long id) {
        return bookService.findById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Book create(@RequestBody Book book) {
        return bookService.create(book);
    }

    @PutMapping("/{id}")
    public Book update(@PathVariable Long id, @RequestBody Book book) {
        return bookService.update(id, book);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        bookService.delete(id);
    }
}

Testing with curl

# Create a book
curl -X POST http://localhost:8080/api/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Clean Code","author":"Robert Martin","isbn":"978-0132350884","price":39.99}'

# Get all books
curl http://localhost:8080/api/books

# Get by ID
curl http://localhost:8080/api/books/1

# Update
curl -X PUT http://localhost:8080/api/books/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Clean Code","author":"Robert C. Martin","isbn":"978-0132350884","price":34.99}'

# Delete
curl -X DELETE http://localhost:8080/api/books/1

JSON Serialization (Jackson)

Spring Boot uses Jackson to convert Java objects ↔ JSON automatically:

// Java object
Book book = new Book("Clean Code", "Robert Martin", "978-0132350884", new BigDecimal("39.99"));
{
  "id": 1,
  "title": "Clean Code",
  "author": "Robert Martin",
  "isbn": "978-0132350884",
  "price": 39.99,
  "publishedDate": null
}

Customizing Jackson

// Exclude null fields
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Book { }

// Rename a field
@JsonProperty("book_title")
private String title;

// Ignore a field
@JsonIgnore
private String internalCode;

// Date format
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate publishedDate;

Response Status Codes

Operation Success Code Error Code
GET (found) 200 OK 404 Not Found
POST (created) 201 Created 400 Bad Request
PUT (updated) 200 OK 404 Not Found
DELETE 204 No Content 404 Not Found

Key Takeaways

  1. @RestController + @RequestMapping = REST API endpoint.
  2. Jackson handles JSON serialization/deserialization automatically.
  3. Use @ResponseStatus to set HTTP status codes.
  4. Follow REST conventions: proper HTTP methods, meaningful URLs, correct status codes.
  5. Layer: Controller → Service → Repository.

What’s Next?

Next: REST Annotations Deep Dive — @GetMapping, ResponseEntity, content negotiation.

Updated: