REST and Spring Boot: Automating and Simplifying Web Development: Part 2

Diego Weidle Rost
25 min readAug 1, 2021


This is the second and last part of my blog posts about REST and Spring Boot, the HOW. As promised in part 1, I would do a practical demonstration of the theory covered. If you want to know more about REST and Spring, and learn a little about myself, make sure to read the first part of this post:

In this second part, we will be building a small application supporting the REST architecture using Spring Boot framework, Maven, Postgres, and Postman to test our API communication. However, I will not be covering maven and the database part in-depth; I will only briefly mention what you need to install to support our application structure. I imagine you have an idea of how to install a database. You can download the script from the .database package from my GitHub (link ahead), so you don’t need to worry about writing DML or DDL SQL commands.

As a bonus (not as detailed as the rest of the post), I will build this application with support to MVC architecture and build the frontend (or view) with Thymeleaf.

I will provide the source code of the application if you don’t feel like following along but want to have access to the code used in this demo as a reference to your future projects or just for studying purposes. I recommend following along with the videos. You can find the source code in my GitHub repository:

After each code section, I will also provide the direct link to each file in GitHub.

In addition, each part is accompanied by a video if you want a visual guide to help you. However, the video will not go as in-depth as this post, so make sure to read the post to have a better understanding of what is going on. Make sure to stop when a section ends and continue reading (the section time is below each video).

This post got way longer than initially expected, but I tried to explain with as many details as I could to make sure you understand. I am sure you will be able to build a basic REST + MVC application after following along.

What is recommended to have to follow along with me in this demo:

  • Basic Java knowledge.
  • Understanding HTTP requests and responses will help to make it easier to understand some sections.
  • JDK 11. You can use a lower version, but I cannot guarantee it will support all features we will be using to build our application.
  • Patience. I am not an instructor. I am just a student like you who is still learning about REST and Spring, and I just want to share with you what I have learned so far.
  • If you notice I wrote something wrong (grammatically or technically) please let me know so I can correct and make this a better experience for future readers.
Section 0: Introduction (0:00 to 1:08)

Structure of our Spring application

Before we begin, let’s take a look at how the structure of our Spring Boot RESTful application will look like once we get to the end of this demo, so you get more familiarized with some of the terms we will be using through this tutorial.

The finished structure

As you can see, we do not have many classes in our application. In fact, “NoteController” and “PersonController” are not actually part of the REST application but will be part of our bonus MVC Spring application. Let me go quickly through each package so you have an idea of what their purpose is:

  • controller: These are the classes that make our REST requests possible. They define the methods and endpoints for our REST API, or the URL path to access our REST methods.
  • dataaccess: Holds the classes responsible for managing data to and from our database. Not much here since we are using the powerful Spring Data (more on that later).
  • database: This package contains the script to run to create the starting database we will be using with our application.
  • model: Classes representing the entities in our database. With Spring Data and JPA we map an entity from the database and convert it to an object.
  • service: Holds the classes responsible for the business logic of our application. They “sanitize” requests before they go to the dataaccess classes.

Our application is based on one of the Labs we did for our Web Applications class during the third semester of our program. The application is just a list of people and each person has some notes. These people and their notes can be edited, deleted, or we can add new ones to the list.

We will implement a REST API so we can do all these operations remotely, technically through another application (we will simulate this with Postman).

After we have our main objective completed, which is to have a complete RESTful application, we will build a basic interface to support an MVC application, so we can manage our data through a GUI.

Getting Ready

If you want to follow along with me, you will need the following tools:

  • An IDE with support to Spring Boot. I will be using IntelliJ. If you are a student like me, you can have access to the Ultimate version for free while you are a student. In fact, you can have the entire JetBrains package for free: Otherwise, you can download a 30 days trial, or the community version (the only limitation is not having the Spring Initializer integrated):
  • A RDBMS to store our data. I use Postgres. If you want to use the script provided you need Postgres because of the syntax used. Postgres is open source and I find the syntax better than MySQL for example. You can download it here:

Like I mentioned before, I will not be providing installation instructions, but I believe you should have no problems with the installation. If you find any problems, please let me know in the comments. Once you have these ready to go, you can follow along with me. I highly recommend following along because I find this the best way to learn something new!

Starting the Project

Section 1: Starting the Project / .database (1:09 to 5:17)

There are many ways to start this project:

  • You can clone it from my GitHub repository (Not recommended, since you will get the project done and will not be able to follow along).
  • You can start it from scratch and try to figure out how to build a Maven pom.xml file (Please don’t)
  • Or, my favourite, use Spring Initializr! You can use the one built into IntelliJ, by going to File > New > Project > Spring Initializr, or going directly to the Initializr website:
IntelliJ Initializr Initializr

The settings in the screenshots are the ones we are going to use:

  • Maven (for dependencies)
  • Java 11
  • Spring Boot 2.5.2
  • Metadata is up to you, I will leave the default data for simplicity
  • Packaging does not matter in our case; you can leave it as Jar

By clicking next in IntelliJ or just to the right on the website, you can choose initial dependencies to add to your project. We want the following dependencies in our project:

IntelliJ Dependencies
  • Spring Web: includes a Tomcat embedded server, so we don’t need to install Tomcat separately!
  • PostgreSQL: the database communication driver. If not using Postgres, select the corresponding one to your RDBMS
  • Spring Data JPA: make our life easier when managing data in our database
  • Thymeleaf: for creating templates for our MVC frontend bonus Dependencies

After adding the dependencies, click “finish” on IntelliJ or “Generate” on the website to build your initial project. IntelliJ will open the project right away and you can start codding. If you are using the website, it will generate a Zip file with your project folder inside, which you can open with IntelliJ.

Resolving dependencies

The first time you start your project, it will take a little while to load since it needs to resolve dependencies and index the library.

As you see in the project folders, the Initializr already generated all the folders we need and, most importantly, the pom.xml file with all dependencies we selected earlier.

The generated project

By the way, if you want a dark theme and have not selected it during IntelliJ installation, you can go to File > Settings > Appearance & Behavior > Theme > Darcula.

Build configuration

Another setting I think is essential to change, is how IntelliJ reloads dependencies if you change any of them during the project. If you leave the default setting, every time you change a dependency, you need to right-click the pom.xml file and reload the project. We want that to be done automatically if any changes are made: Go to the settings, then Build, Execution, Deployment > Build Tools > Check “Reload project after changes in the build scripts: External changes”. We will probably not be going to add or remove any dependencies but change this anyways to your future projects.

Now that you have your folder structure ready, let’s get started with the fun part! I will start from the backend (database package) and go up the layers until we reach the controller package (layer) and later work on the resources folder (where our fronted will be located). Some people prefer to start with the frontend, but I like to have things ready when I need them, like the database.


First, create a package called “database”. Not much here, since you should already have your database installed. I like to keep any SQL scripts in this package. You can put the notesdb.sql script you download inside this package, and then open the script to add the data to your database. When you open it, you probably will have to select a data source, which is our database connection.

Data source added

You can do that by clicking on the yellow bar at the top that will popup or if not, just right-click on the script and select “run”, this will bring the same window to select the data source. Click the plus sign at the top left, select Postgres. After this, type the username and password you chose during Postgres setup, give it a name, and the other default settings like host and port are probably already correct. You can then click on “Test Connection” once you are done to make sure the connection is working fine.

After you finish with the data source configuration, you can try to run the script again and this time it will ask you to select the target data source. Click the plus sign and select the schema you want. I will just select the public one. Click run, and you will have our sample data in your database.

Data source assigned

This is how our application structure is looking so far:

The application so far

On to the next layer!


The second package we will be creating is the model package. This package contains the objects mapped from our database: Note and Person.

IntelliJ has a feature to create those automatically, but I prefer to create them manually to customize them a little. In addition, you learn more about annotations and how the object is mapped from the database by creating models manually.


Section 2: .model: Person (5:18 to 12:32)

The first model we will code is the Person. Inside your model package, add a new class called “Person”.

This is the code we need in our Person class (I strongly encourage you to follow along with the video and not just copy and paste the code):

Don’t forget to add getters and setters for everything and import anything if required. We might not use all of them, but for simplicity, just add to all attributes. You can easily do that by pressing “Alt + Insert” > Getter and Setter > Select all of them.

When you add the code, you will notice the table name and all column names are highlighted. Mouse over “person” for a second, and select “Assign Data Sources”, then select your database. This will associate our database with this model and fix the warnings.

Assigning a data source to a model

As mentioned in the beginning, I believe you already know basic Java, so I will not be covering things you are already used to. I will be explaining different elements in the code you might never have heard of before.

  • @Entity: This means this class represents an entity from the database
  • @Table(name=”person”, schema = “public”): Table name in the database and schema the table is located at (If you set your data source other than public make sure to change it appropriately)
  • @Id: This attribute is a primary key in the database table
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Method to increment the ID every time a new Person is created.
  • @Column(name = “email”, nullable = false, length = -1): Name of the column this attribute is representing, cannot be null, size not specified. Postgres allows having columns without a size.
  • @Basic: A regular column in the database table
  • @JsonManagedReference: I will not get in-depth with this. But if we don’t add this, we will have problems with our frontend later
  • @OneToMany: Every person can have many notes. All notes owned by this person will be added to this list.
  • cascade = CascadeType.ALL: When this person is removed, delete all notes belonging to this person.
  • mappedBy = “owner”: Attribute for the foreign key in the Note model.
  • fetch = FetchType.EAGER): This means that when a Person model is loaded, it will also load all notes belonging to this Person.

We have an error in our list of notes because, well, we still do not have a Note model. We will fix this now.


Section 3: .model: Note (12:33 to 16:58)

Our Note model is very similar to Person, so I will not be repeating everything other than a few different details.

Inside your model package, add a new class called “Person”.

This is the code we need in our Note class:

Don’t forget to add getters and setters and imports if required.

As you can see, the structure is kind of the same as Person. The only difference here is that we have a “many to one” relationship instead of “one to many” and the “JoinColumn” annotation.

  • @JoinColumn: Connects this model to the Person model.
  • name = “owner”: This is referencing our “mappedBy” parameter from the Person class, mapped to “owner”
  • referencedColumnName = “person_id”): This is the foreign key in the Person model/table
  • @ManyToOne: There are many notes to one person
  • optional = false: The note must belong to a person
  • fetch = FetchType.LAZY: Load the notes from the database only when required, not as soon as a person is loaded


Section 4: .dataaccess (16:59 to 21:41)

The third package we will be creating is the dataaccess package. Not much in this package, since Spring Data does most of the work for us.


First, let's create a repository for Person, so we can get the data from the database. Create an INTERFACE (Make sure to select Interface when creating the class) under the dataaaccess packaged called PersonRepository.

Creating the interface

This is the code to add to our Interface (Again, I recommend you to follow along with the video and not copying the code):

Yes, that is all the code you need!

The @Repository is just so Spring knows this is going to be a repository.

Extending “JpaRepository” is what makes the magic happens. The parameters “Person” and “Long” are our Person model and the type of primary key we are using; Long in our case.

If you want, you can take a look at the JpaRepository class or read the documentation to understand more about how this magic happens. The important thing to know is that Spring Data is saving you a lot of time by automatically doing all those methods to manage data you normally have to do using JDBC or the regular JPA.


Same thing as above, for our Note model. Create the NoteRepository interface, and add the code to your class:

This time, you need to declare two different methods that are not in JpaRepository by default:

  • The findByOwner method finds the notes based on a Person. The default “findBy” method uses the primary key to find the notes instead of a Person model.
  • The findByOwnerAndNoteId does something similar, taking both the primary key and a Person object.

You will understand more about the purposes of these methods later.

You may never have heard about “Optional” before. Optional basically saves you the trouble of having to do a try-catch for a NullPointerException or checking if the object returned is null. If the object is null, we do something different with the object. More on that later.

Our application so far:

Our application so far


the service package is where things start to get a little more complicated. But not too much!

Here we will have the methods needed to access the database, using the repositories we just “coded”. This is where normally you would write your business logic or code for validating data for bad input and things like that. However, I will keep it simple and skip almost all of the validation. Make sure to always validate your application input though.


Section 5: .service: PersonService (21:42 to 29:24)

As you have probably guessed, create a PersonService class under your service package.

Let’s add the code first, and I will explain to you what is going on after the code:

  • @Service — This is just to indicate this class holds the business logic of our application.
  • @Autowired — Dependency Injection. This, together with the constructor, means that this class cannot exist without a personRepository. It makes this class dependable on another, a concept similar to composition.
  • findAll() — As you imagined, returns a list with all Person objects.
  • findById(long personId) — Here you can see the “Optional” object in action. If a Person is not found with the given ID, the “.orElseThrow” throws an exception with a message. This is the same as declaring a Person object and if (object is null) throwing a new exception.
  • save(Person person) — Stores a Person into the database using Spring Data magic.
  • deleteById(long personId) — The magic now removes a Person.
  • update(Long personId, Person person) — Updates a Person’s data with newly provided data. The provided Person object contains new data to update the Person object found with the provided “personId”


Section 6: .service: NoteService (29:25 to 38:44)

Create the NoteService class under newlyservice package and add the following code:

Other than the two custom methods we added earlier in our NotesRepository, everything is very similar to our PersonService and I believe you know what is going on by now. If not, take your time to compare these methods with my explanation from earlier and make sure you understand.

You will understand the purpose of the custom methods in the next package.


And we finally got to the last and most important package of our API! This is the package that makes our API work and therefore, probably the most complicated one to explain and understand. But do not let this scare you, it is still simple compared to what you would need to do without Spring.

This package is the one that allows external applications to access and modify the data of our application through REST controllers. In a real scenario, you would need to have some kind of security token to authorize external applications, such as OAuth.

This package is also where we will add our MVC controllers to make the bonus frontend material work. But for now, let's focus on our REST API.


Section 7: .controller: PersonRestController (38:45 to 47:25)

Add a PersonRestController class under your controller package and add the code below.

As always, try to follow along with the videos instead of just copy and pasting:

Lots of annotations to explain here! However, if you already read or watched the first part of this post you probably have a good idea of what some of them mean.

  • @RestController — Not only this annotation means that this class is a REST controller, but it also eliminates some of the code we would have to do if we were using a @Controller annotation.
  • @RequestMapping(“api/people”) — This is the main endpoint of the REST API to access our data. An endpoint is an address required to access certain data in our application. Since we are testing our application locally, the full address would look like this: http://localhost:8080/api/people.
  • @GetMapping — In the first Get, since we are not specifying an endpoint, making a GET request to api/people will return a list of people in a JSON format. It simply uses our findAll method in services to get the list and return it to the requester.
  • The second Get is using a “personId” surrounded by braces as an endpoint. As you can see in the method declaration, @ParthVariable specifies that we want to use the endpoint as the method parameter. We can then use our findById method from the service class to return the JSON containing the Person with that ID. api/people/1 for example, will get the Person with ID 1.
  • @PostMapping — Our post method also does not require a path and making a POST request to api/people will save a new Person. As you can see the @RequestBody annotation creates a Person object from the data in the response body. The method then uses the save method from our service class to send this new Person to the database.
  • @PutMapping — A PUT request to api/people/1 for example, will update Person with ID 1 with the data from the response body. It also gets the path variable to find the person to be updated. If you remember, we created a method in our service class earlier that needs this Person object to update an existing Person.
  • @DeleteMapping — I am pretty sure you know what this one does. A DELETE request to api/people/1 for example will remove the Person with ID 1 from our database.


Section 8: .controller: NoteRestController (47:25 to 56:24)

And finally, the last piece of our REST API, the NoteRestController. Add the corresponding class to the controller package and add the code below:

As you can see, it is similar to our PersonRestController, but it is a little more complicated. As always, I will cover the points that are different from the other controller.

  • @Autowired — Different from the other times we used this annotation, you can see we have two dependencies on this controller. I just wanted to emphasize that we can have more than one dependency.
  • @GetMapping — The first Get is also getting all notes, like the Person getting all people. However, we need a path variable this time, since we need to know from which Person we are getting the notes from.
  • The second Get is also a little different. We are using two path variables. We need both IDs here because even though we are getting just one note, we still need to know from who. Here is where we use that custom method we added to our repository and later to the service class. The address will look like this: api/notes/1/1 for example. Note 1 from Person 1.
  • @PostMapping — Different from the Person POST request, we again need the personId path variable so we know to which Person the note will be added.

The remaining two methods work exactly like the corresponding Person methods.

And this concludes our REST API application! This is what the final structure for the REST API should look like:

The REST application done


Section 9: Testing With Postman (56:25 to End)

Now that we concluded our REST API, let's test it to see if everything is working as it should!

There are two ways we can test our application:

  1. Using the embedded request tester in IntelliJ: If you open any of our controller classes, you will notice a green icon with a little planet beside each request method. If you click any of them, it will open the tester window.
Using the embedded tester

2. Using Postman: Postman is a very useful program for testing requests. It is very simple to use and I will be showing you how to use it to test some requests to our application.

The first thing you must do is run our application so Spring start Tomcat and have our API server up and running. Before we do that though, we need to add a few configurations to our “”, found under the “resources” folder.

Add the following lines to this file:

I am using Postgres with default configurations. If you used another RDBMS, or changed any configurations during Postgres setup, you might have to change some configurations accordingly.

After you do that, just select “DemoApplication” under the dropdown beside the play button and press the play button after that.

Running the app

You will see a bunch of messages running in the console. If you got no error and something like “Started DemoApplication in…” the application started successfully. Congratulations!

Now, let's simulate some requests in Postman. I found Postman works better with Firefox. On Chrome I could not make the Postman website detect my Postman Desktop Agent.

First, create an account and download Postman from

After you are logged in, it will bring you to This is where you can simulate the requests.

First, create a new Workspace and open this Workspace:

Postman Workspace

Once you get to your Workspace you will see a bunch of options and features, but for now, we are only interested in simulating requests. There is more than one way of doing this. If you already have a request in your history tab to the left, you can just click one of these requests and make a new one. But you probably don’t have any requests since you just started, so you can click the “New” button, besides your Workspace name:

New request

or clicking “Create a request”, under “Get started”:

Create a request

Before we start with the requests, make sure you have your Postman Desktop Agent running in your machine so you can do unlimited local requests. Otherwise, there is a limitation on a free account using Postman servers.

Running the Postman Agent

If you get it running correctly you will see the website detects the Desktop Agent (assuming you are using Firefox as mentioned at the beginning of this section).

You can now do unlimited local requests. Let's finally start with our requests! On the first request, I will show you more details so you understand the different points you can check with Postman.

First, let's test our GET requests and retrieve some data from our database:

Sending a GET request
GET people response

As you can see at the top right, we got a 200 status code, which means our API is working for this request. You can also see it returns a JSON response containing all the people and their notes from our database.

GET people/1 response

Here we get only the user with ID 1 and their notes.

GET notes 1 response

This time we got only the notes for the Person with ID 1

GET note 1 from person 1 response

And now only the first note for Person 1

Second, let's see if our API is accepting POST requests and adding new content to our database. “Select POST > Body > raw > JSON

Sending a POST request

After that, add the following JSON to the body (or anything else you want following a JSON format with these fields) and click SEND


“email”: “”,

“active”: true,

“firstName”: “Post”,

“lastName”: “Test”,

“password”: “password”


If you got a status 200 response, the data has been added to our database. You can confirm by going to IntelliJ, clicking on “Database” in the top right corner, expanding the database until you find “Person”, and right-clicking on it and selecting “Edit Data”.

Edit Data
New row added to database with POST request

As you can see, the data from our POST request has been added successfully to our database!

I believe you can now picture where this is going, so I will just do two more examples: One for a PUT request, and another for a DELETE request.

I want to edit or recently added Person (ID 3) with this data:


“email”: “”,

“active”: false,

“firstName”: “Put”,

“lastName”: “Test”,

“password”: “password”


PUT in Postman is the same as POST, we just need to specify the Person we want to edit with the endpoint.

PUT request

After adding the data above and clicking Send, we should get the 200 response, meaning our database data has been successfully updated. If you still have the “Edit Data” tab opened from earlier in IntelliJ, you can click the refresh button to see the updated data.

Updated data after PUT request

Lastly, let's remove our added Person with a DELETE request. Basically, just change the previous request to DELETE and press Send.

DELETE request
Person removed

As you can see, the Person we added is gone from our database, confirming the success of the operation.

Now that you know how to do all tests, you can play around and do some requests to Notes maybe, to make sure you understand how it works.

That is everything for our REST API! If you got his far, congratulations! I hope this tutorial, together with the first post, was useful for you to learn what is REST and Spring Boot, and to know how to make a RESTful application using Spring Boot.

If you followed along and got it working, let me know in the comments! Also, let me know if there is anything I can improve in this tutorial. Thank you very much!

Bonus: Adding a Frontend

Now that we have built a REST API so external applications can manage our data, let’s make a simple interface so we can also manage our data with a GUI.

I decided to do this part because initially, I was confused, and I thought we could use REST API to send data to the same application interface. In fact, we can, but you would need JavaScript and a bunch of code, which is not ideal. It is easier to use an MVC Controller for this. It is similar, but the methods work differently. I want to share this with you, so you don’t get confused as well.

This time I will not go too much in-depth since this tutorial is already way too long. I just want to give you a brief idea of how this works.

To make this work, we are going to use Thymeleaf (remember that dependence we added in the very beginning?). Thymeleaf is similar to JSTL but more powerful.

Instead of generating a raw JSON response like a REST controller, an MVC controller redirects this JSON to a Thymleaf HTML template and loads this template with the data.

To start, let's create the two controllers we need. Both use the same services as the REST controllers, they just process the data differently.


First, add a PersonController class to our controller package. Then add this code to the new controller:

The first thing you can notice is that we have @Controller instead of @RestController.

You can also see that the address is /people and not /people/api like in the REST controller.

The mappings are pointing to the actions instead of a noun or IDs.

Here we are using a Model as a parameter. This model is what goes to the templates so we can use them in the frontend.

The methods take a Model as a parameter, which is loaded into the template, which is returned and loaded when we go to the mapped address.

The last two are not loading a template, they are just redirecting us back to /people to preventing duplicates from being added to the database.

The rest I think you can figure out by yourself. If not, leave a question and I will try to get back to you.


Same thing with the NoteController. Add the new controller class and add the code to it:

Everything here is very similar to the PersonController and I believe you can understand what is going on. The only thing that might be a little more confusing is the redirects because we need to know to which Person the note belongs to return to the right page after the operation is concluded.

This is how our controller package looks now:

New controllers added


Under the resources folder, there are two subfolders: static and templates.

The static folder holds pages that will not change and are not dynamically loaded. For our application, we only need an index.html that will only redirect the user to /people. So, add the index page and the following code to it:

That is everything. We need nothing else in our index.


Here is where the templates that our mappings are going to load are stored.

First, create a subfolder called “people”. Inside it, add our first template: people.html. After creating the template, add the following code to it:

I know it does not look good. I have not added any CSS or Bootstrap to it for simplicity, but it works as it should.

As you can see, it works similar to JSTL, but is simpler. It takes the Model “people” we loaded in out GetMapping, loops through each person, and adds an entry for each field in a table. The links under “Manage” are using the paths we specified in our controller class.

Now, add “person-form.html” to the people folder and add this code:

If you compare the mappings and models you can understand what is going on there.

Lastly, do the same thing, but for notes: Create a notes folder under templates and add notes.html and note-form.html.

This is the code for notes.html:

And this is the code for note-form.html:

The idea is the same as the Person templates. Study it a little and I am sure you will understand everything with what you have learned so far.

This is how our resources folder will look in the end:

The Resources folder

You can go to http://localhost:8080 and you will see you now have an (ugly) functional frontend! Test everything and you can see you can manage database data the same way you did before with the API in Postman.

Sorry for not getting into more details with Thymeleaf, but this bonus was not planned to be added in the first place, I just thought it would be a nice addition since I missed not knowing how to have a frontend to my REST application.

I hope you enjoyed this bonus and that it helped you to understand how to make a front end with Spring and Thymelaf.

Again, if you saw something wrong don’t hesitate to mention it in the comments!



Diego Weidle Rost

I'm a passionate software developer always eager to learn about technology. Here I hope to also share something of what I learn with fellow researchers