Bookish

Learning goals

  • Using relational databses
  • Object-relational mappers (ORMs)
  • Linking different sources of data
  • The MVC model (Model-View-Controller)
  • Developing effective user interfaces

Programs Used

Overview

We’re going to build a simple library management system. We’d like it to support the following features:

  • Book Management
    • The librarian can browse the catalogue of books
      • Considerations: How are the books going to be sorted? By title? By author?
      • Could we add a search feature?
    • The librarian can edit the catalogue of books
      • Add new books
      • Update the details of a book
      • Add a copy of an existing book
      • Delete a copy of a book
    • For each book, we will need details of
      • The total number of copies of that book
      • The number of copies that are currently available
      • Which users have borrowed the other copies
  • Member Management
    • The librarian can see a list of library members
      • With a list of the books that member currently has checked out
    • The librarian can add a new member
    • The librarian can edit the details of an existing member
  • Checking books in / out
    • The librarian can check out a copy of a book to a user
    • The librarian can check a copy of a book back in
      • Notification for late returns?

Tip

There isn’t a lot of time to implement all of these features, so don’t worry if you don’t get all of them done. What’s important for this exercise is that you gain an understanding of how data moves through your system, and how to manipulate objects in your database.

Setup

The starter code

First, clone the the Bookish – Java repo and follow the instructions laid out in the readme to run it. Though running it won’t achieve anything at this moment in time.

The starter code you have been provided with uses Spring to provide a REST API that will sit on top of your database. The idea here is to provide endpoints that can be called to GET, POST, PUT, or DELETE data from/to your database, however, none of these endpoints have been implemented yet.

It’s worth going through what we do have in the starter code to make sure we understand what we have before we try and implement these endpoints.

Controllers

The home of all of the controllers for the application. Right now this is empty, but it is where you you will define and design your endpoints.

Routing

You can declare actions in these controllers. An action is really just a method that returns an Response.

You can specify the endpoint URLs yourself by adding annotations to the controller and action like this:

@RequestMapping("/persons")
class PersonController {
    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        // ...
    }
    @PostMapping
    public void add(@RequestBody Person person) {
        // ...
    }
}

Models

Much less to talk about here – models are nice and boring!

Models are just classes that define what data the app should contain. Have a look at Book.java in the models\database folder to get an idea of what one looks like.

Repositories

The repository acts as a link between the database and your code, and mainly exists to reduce the amount of boilerplate code you need to write. They usually extend of an existing repository class such as CrudRepository or JpaRepository which will give access to some pre-existing methods such as .findAll() and .save(), the purposes of which I’m sure you can imagine.

Take a look at BookRepository in the repositories folder to get an idea of what one looks like. Note that we extend from JpaRepository<Book, Long> (marking the type of the object and its id) and have already declared a method to get a Book by its Title without having to write any boilerplate code to implement it.

Migrations

Migrations are just a series of SQL scripts that are run in order to build up the structure of our database. We’re using Flyway as our Migrations Framework to help make this easier, it will automatically run all new migrations every time the app is run.

Have a look at V1__CreateBooksTable.sql in the resources/db/migrations folder to get an idea of what a migration looks like, this one just creates a simple table to represent a book.

application.properties

This file simply configures Spring, take a look (its in the resources foler). You’ll be able to see that we’ve told Spring what port to host the server on, what SQL dialect we are using, and all the information it needs to access the database.

Setting up PostgreSQL

If you haven’t got PostgreSQL installed already, you’ll need to download and install it from the PostgreSQL website. Leave all the options as the default, but untick “Launch Stack Builder at exit” on the last screen.

Warning

Make sure you remember the username/password you set when installing!

When you installed PostgreSQL Server, you may have also installed pgAdmin (if not, download and install it from here). This will allow you to manage your PostgreSQL server.

Navigate to Servers > PostgreSQL > Login/Group Roles (when you’re asked for a PostgreSQL password, put in the one you chose above for installing Postgres): right-click and create a new login/group role named bookish with a suitable password and privileges “Can login” and “Create databases”. 

Go to Servers > PostgreSQL > Databases, right-click and create a new database named bookish with owner bookish.

Make sure you can fire up PostgreSQL Server and connect to the database using pgAdmin. If you can’t, please speak to your trainer to help get it sorted.

Designing the database

You’ll need to come up with a database structure (i.e. what tables you want in your database. This is similar to deciding what classes to have in your application). Think through your database design. Work out what tables you need to support the scenarios outlined above. Discuss this with your trainers before you create things in the database.

You’re going to be using an ORM (object-relational mapping) to help manage the database. These help to abstract the interactions with the database by creating Models to represent the tables in your database, and Migrations to manage changes to the structure of your database.

You’ve already been provided with migration to create a book table and a Book model (though they likely need a few additions), use these as inspiration to create models and migrations to define the rest of your database structure.

When writing code to connect to the database, the username/password should be the ones you set up previously (i.e. bookish / <password>) – at this stage we can put these values directly in our code, but remember this is bad practice; such values should be externalised and passwords should be encrypted.

Building an API

Let’s start building an API that your frontend can call to in order to interact with the database.

You’ll want to start by implementing a basic endpoint: GETing all the books available in the library.

First, create a new controller called BookController. Mark this controller with the @RestController attribute. This allows the controller to be automatically detected by the web app and indicates that every method of the controller should inherit the @ResponseBody annotation (this means the method will return JSON rather than trying to render an HTML template). The controller will need a BookRepository instance variable so that it can query the database.

Within the BookController, define a new method called getBooks that returns a list of Books. This method should also be marked with the @GetMapping("<route>") annotation ("books" is probably a sensible value for <route>). This method should return a list of all the Books in the BookRepository.

To test your endpoint is working, simply run the app with ./gradlew bootRun and use your browser or a tool like Postman to test your API endpoint and check that it’s all working as intended. Provided you followed the instructions above the endpoint url should be http://localhost:<port>/books. It should return a JSON-formatted list of all the books in your database.

If you’re struggling, you may find the Spring RESTful Web service documentation, and some parts of the Spring Web MVC docs to be helpful.

Once you’re satisfied that that’s working, have a go at implementing a few more of the features in your API, starting with a POST endpoint to add a new book to the library.

As usual, be sure to ask your trainer for help if you’re not sure of something!

Build the website

Now that you’ve got the API, it’s time to start building the frontend of the website. Otherwise, people won’t actually be able to access the books they want!

We’re going to be using Vite – a dev server and bundler – to make getting everything working a bit easier. Open your terminal, go to your work directory and run

npm create vite@latest -- --template vanilla

NPM should offer to install Vite if you don’t already have it. It should prompt you for a name for the project, and then create a directory with the same name that should be populated with a basic template for a site. If you go to that directory, you should see a few different files. The ones you should pay most attention to are:

  • index.html – This is where the majority of the layout of the site is defined
  • main.js – This is where interactive and dynamic parts of the site are written
  • style.css – This contains the styling for your site

Currently, they’ll have some example content from the template. To preview the site, you enter the file that vite has created and install dependencies and run the dev server by running the following in your terminal:

npm install
npm run dev

Go to the address it shows in your browser, and a site should appear! Once you’re happy that’s working, open index.html. You’ll notice a div with id="app". Start building out a basic structure for your site, with a simple header and page layout, replacing the div that’s currently there. As you’re doing this, try to keep accessibility in mind and use correct semantic HTML elements when you can. Once you’ve got a basic structure, you can try styling it a bit too by editing style.css.

Adding content

You’ve got a basic structure for your site, but there’s no content yet! You’ve already made an API route for listing all the available books, so now you can put it to use. You’ll probably find it helpful to create a container div with a unique, descriptive id that you can use to put all the content in. If you open main.js, you’ll notice the code already there, which is used to add content to that original div that you replaced. Feel free to remove or comment out the code – make sure you leave the import './style.css' though!

Now, add some code to your main.js to call your API, retrieve the list of available books, and populate the container you made when the site loads. You’ll want to make use of document.querySelector to get the container element (you can look at the template code for an example of how to use it), and the Fetch API to make an HTTP request to your API. This should end up looking something like:

fetch("localhost:3001/books")
    .then(response => response.json())
    .then(data => setMyData(data));

Once you’ve done this, you should be able to refresh the page, and see all the books listed on your page.

Interactive sites

It’s good that people can see the available books, but how are they going to add new ones to the library? You should have an API endpoint for this too, so all you need is an interface to interact with it. Add a form to your page so that people can submit new books. You’ll want to use the form HTML tag. Have a look through the MDN docs for the form tag and see if you can figure out how to submit data to your API. Remember to make sure you’ve set the correct method to match what you defined for your API.

Once you’re done with that, you should be able to submit a book, refresh the page, and have it appear along with all the other books!