Building the Library Catalog API: A Step-by-Step Guide

Welcome to the official solution for our first backend coding challenge! This Spring Boot REST API tutorial will guide you through building a Library Catalog API from the ground up. Our strategy is to implement one endpoint at a time, making the process clear and manageable.

Before writing any code, a great developer starts with the data. Let’s design our database schema.


## Step 1: Designing Our Database Schema 🏛️

Our database architecture is built around a classic one-to-many relationship. This structure is efficient and prevents data duplication.

Database schema for our Spring Boot REST API tutorial.

#### 1. Authors Table (authors)

id (Primary Key)name
1George Orwell
2J.K. Rowling

#### 2. Books Table (books)

id (Primary Key)titleauthor_id (Foreign Key)
10119841
102Animal Farm1
103Harry Potter and the Sorcerer’s Stone2

## Step 2: Implementing the First Endpoint: Create an Author

With our database planned, we’ll build the POST /api/authors endpoint to add new authors. The client sends a JSON object, and our API saves it to the database.

Request Body:

JSON

{
  "name": "J.K. Rowling"
}

## Step 3: Code Implementation for the Create Author Endpoint 👨‍💻

It’s time to write the Java code. We’ll use a layered architecture (Controller and Service) for clean, maintainable code.

Layered architecture in our Spring Boot REST API tutorial.

#### The Code: Controller and Service Layers

Controller (LibraryController.java)

Java

@RestController
public class LibraryController {
    private final LibraryService libraryService;

    public LibraryController(LibraryService libraryService) {
        this.libraryService = libraryService;
    }

    @PostMapping("/api/authors")
    public ResponseEntity<AuthorDto> createAuthor(@RequestBody AuthorDto authorDto) {
        AuthorDto createdAuthor = libraryService.createAuthor(authorDto);
        return new ResponseEntity<>(createdAuthor, HttpStatus.CREATED);
    }
}

Service (LibraryService.java)

Java

@Service
public class LibraryService {
    private final AuthorRepository authorRepository;

    public LibraryService(AuthorRepository authorRepository) {
        this.authorRepository = authorRepository;
    }

    public AuthorDto createAuthor(AuthorDto authorDto) {
        Author author = new Author();
        author.setName(authorDto.getName());
        Author savedAuthor = authorRepository.save(author);
        authorDto.setId(savedAuthor.getId());
        return authorDto;
    }
}

#### How It All Works: The Magic Behind the Scenes

When a request hits your application, Spring’s DispatcherServlet acts as a traffic cop, routing the request to the correct controller. The @RequestBody annotation uses the Jackson library to automatically convert the incoming JSON into a Java AuthorDto object (Deserialization). Spring’s Inversion of Control (IoC) container automatically injects the necessary service instance. Finally, @RestController tells Spring to convert the returned Java object back into JSON (Serialization) and send it in an HTTP response.


## Step 4: Connecting the Data: Implementing the Add Book Endpoint 🔗

Next, we’ll implement POST /api/authors/{authorId}/books. This endpoint handles URL path variables and establishes the one-to-many relationship.

#### The Code: Controller and Service Layers

Controller (LibraryController.java)

Java

@PostMapping("/authors/{authorId}/books")
public ResponseEntity<BookDto> addBookToAuthor(
        @PathVariable Long authorId, 
        @RequestBody BookDto bookDto) {
    BookDto createdBook = libraryService.addBookToAuthor(authorId, bookDto);
    return new ResponseEntity<>(createdBook, HttpStatus.CREATED);
}

Service (LibraryService.java)

Java

public BookDto addBookToAuthor(Long authorId, BookDto bookDto) {
    Author author = authorRepository.findById(authorId)
            .orElseThrow(() -> new EntityNotFoundException("Author not found: " + authorId));

    Book book = new Book();
    book.setTitle(bookDto.getTitle());
    book.setAuthor(author); // The crucial link!

    Book savedBook = bookRepository.save(book);
    bookDto.setId(savedBook.getId());
    return bookDto;
}

## Step 5: Putting It All Together: The Get Author Profile Endpoint 🎯

Our final task is to create the GET /api/authors/{authorId} endpoint to fetch an author and all their associated books.

#### The Code: Controller and Service Layers

Controller (LibraryController.java)

Java

@GetMapping("/authors/{authorId}")
public ResponseEntity<AuthorDto> getAuthorById(@PathVariable Long authorId) {
    AuthorDto authorProfile = libraryService.getAuthorById(authorId);
    return ResponseEntity.ok(authorProfile);
}

Service (LibraryService.java)

Java

public AuthorDto getAuthorById(Long authorId) {
    Author author = authorRepository.findById(authorId)
            .orElseThrow(() -> new EntityNotFoundException("Author not found: " + authorId));

    AuthorDto authorDto = new AuthorDto();
    authorDto.setId(author.getId());
    authorDto.setName(author.getName());

    List<BookDto> bookDtos = author.getBooks().stream().map(book -> {
        BookDto bookDto = new BookDto();
        bookDto.setId(book.getId());
        bookDto.setTitle(book.getTitle());
        return bookDto;
    }).collect(Collectors.toList());
    
    authorDto.setBooks(bookDtos);
    return authorDto;
}

#### How It Works: Lazy Loading

By default, JPA uses Lazy Loading. When you fetch an author, Hibernate doesn’t fetch their books immediately. It only runs the second query to get the books when you explicitly call author.getBooks(), which is a smart way to improve performance.


## Project Complete: API Summary & Source Code 🏆

Congratulations! You’ve successfully completed this Spring Boot REST API tutorial. You’ve built a functional API with three endpoints. Here is a final summary for your reference.

#### 1. Create a New Author (POST /api/authors)

Sample Response:

HTTP

HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": 1,
  "name": "George Orwell"
}

#### 2. Add a Book to an Author (POST /api/authors/1/books)

Sample Response:

HTTP

HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": 101,
  "title": "1984"
}

#### 3. Get a Full Author Profile (GET /api/authors/1)

Sample Response:

HTTP

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 1,
  "name": "George Orwell",
  "books": [
    {
      "id": 101,
      "title": "1984"
    },
    {
      "id": 102,
      "title": "Animal Farm"
    }
  ]
}

#### Full Source Code

You can find the entire source code for this project on GitHub.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top