Create a REST API with Spring Boot
Why Spring Boot?
Spring Boot is a framework that bootstraps a Java web application without the XML configuration headaches that come with a typical Spring application. The framework adopts an opinionated approach to configuration by making decisions based on the use cases that fit the majority of modern web apps. In other words, it follows the philosophy of convention over configuration.
With Spring Boot, we can create an application that bundles all dependencies and it’s servlet container so it doesn’t even require a traditional WAR deployment. We’ll have a fat executable JAR that can be run as a standard Linux service.
Dive In to New App
Let’s imagine we’re developing a restaurant reservation application called AnnexResto to compete with OpenTable. Users can create an account, search for restaurants, book a reservation, and receive notifications via SMS or e-mail.
The application consists of the following micro services:
Account
Search
Restaurant
Reservation
Search
Messaging
Each of these micro services has a REST API and its own MySQL database. The services will be accessed by desktop and mobile users via an API gateway.
In this article, we’ll focus on building the API for the Restaurant micro service.
REST Endpoints
Our application will expose the following endpoints:
GET /v1/reservations — Retrieves all reservations
GET /v1/reservations/{id} -Retrieves a specific reservation based on ID
POST /v1/reservations — Creates a new reservation
PUT /v1/reservations/{id} — Updates an existing reservation
DELETE /v1/reservations/{id} — Removes a specific reservation based on ID
Technology Stack
We’ll be using the following technologies for the application:
Spring Tool Suite (STS) 3.83 Better I’ll prefer IntelliJ for Better Work Experience
Spring Boot 1.5.1
Spring Data JPA 1.11
Spring MVC 4.3.6
MySQL 5.7
Maven 3.3.9
Getting Started
There are a few different ways that we can begin development:
Use the Spring Boot CLI
Use the Spring Initalizer tool
Use an IDE like Spring Tool Suite (STS)
We’ll use the Spring Tool Suite for this project. IntelliJ is customized for developing Spring applications with spring boot Plugin in Market Place. It comes with built-in Tomcat server, validation of config files among other handy features that makes life easier.
Installing Spring Tool Suite
Spring Tool Suite comes packaged as a zip file and does not use an install wizard or store anything in the Windows registry. So we can just unzip the file and put it anywhere.
On first launch, it will ask us where we want our workspace to live. This is the directory that holds all of our projects and settings.
Creating the Project
Let’s launch Spring Tool Suite and select File->New -> Spring Starter projector or launch IntelliJ and select File->New -> Maven project
We can configure it as a Maven project and enter the Group, Artifact, and Package as below. Click Next.
The next screen will ask to select our dependencies. These selections will get populated into Maven’s pom.xml file.
For our project, we want the following dependencies:
Core -> Dev Tools
This dependency makes development easier for us by adding some neat features like automatic Tomcat restarts after each file change. Not required but useful to have.
SQL -> JPA, MySQL
These dependencies will allow our app to communicate with a MySQL/MariaDB database. JPA is the Java Persistence framework and will do the heavy lifting on the querying side so we don’t have to write boilerplate SQL statements for routine operations.
Web -> Web
This dependency allows us to create a REST API based on Spring MVC
Click Finish to create the project.
Maven pom.xml
Our project has now been created so let’s look at the Maven configuration file. Double-click on the file in Package Explorer and go to the tab named “pom.xml” in the main window pane.
Parent project
We’ve got a parent project named spring-boot-start-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
This configuration block declares that our project is a child of the parent project and thus inherits a host of default Maven configurations.
Dependencies
Now let’s check out the dependencies section:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
These artifacts (and their own dependencies) have all been downloaded automatically by Maven and the JARs can be viewed by expanding the Maven dependencies under Package Explorer:
Build
In order to make our final JAR executable, we’ll need to add an element named executable into this section:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
Application Class
The Application class containing the main method is created for us automatically by the Project wizard.
package com.codebyamir.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReservationApp {public static void main(String[] args) {
SpringApplication.run(ReservationApp.class, args);
}}
Notice that the class is annotated with @SpringBootApplication. This annotation is shorthand for three annotations: @Configuration, @EnableAutoConfiguration and @ComponentScan.
What does this do for us?
- @Configuration tags the class as a source of bean definitions for the application context.
- @EnableAutoConfiguration tells Spring Boot to start adding beans based on class path settings, other beans, and various property settings.
- @ComponentScan tells Spring to look for other components, configurations, and services in the the com.codebyamir.springboot package.
Notice that there was no XML configuration required.
Our main method delegates to Spring Boot’s SpringApplication class by calling run. SpringApplication will bootstrap our application, starting Spring which will in turn start the embedded Tomcat server.
We need to pass SeatingNow.class as an argument so SpringApplication knows the primary component.
The static run method performs a number of important setup tasks:
Sets the default configuration
Starts Spring application context
Performs classpath scan
Starts Tomcat server
If we run the project as a Java application (Right click project -> Run As -> Spring Boot Application), we see a bunch of output in the console with some fancy ASCII art:
We’ve started Tomcat server on localhost with its default port 8080. Let’s check it out in our browser:
We get an error page from the Tomcat server.
What’s happening here? There is no mapping for “/” so it tries to go to “/error” but there’s no mapping for that either so it returns a 404.
This is normal so no worries. The server is running, but we haven’t defined any API endpoints yet.
Set server.error.whitelabel.enabled=false in application.properties to switch the whitelabel error page off and restore the default error page from Tomcat.
Build better voice apps. Get more articles & interviews from voice technology experts at voicetechpodcast.com
Populate the Database
One of the tenets of micro service architecture is one database per service. So let’s create and initialize the database with some sample data:
CREATE DATABASE Annex_reservation;USE Annex_reservation;CREATE TABLE reservation (
`id` int(1) NOT NULL AUTO_INCREMENT,
`user_id` int(1) NOT NULL,
`party_size` tinyint(1) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`restaurant_id` int(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8;INSERT INTO reservation VALUES
(NULL,100,2,NOW(),800),
(NULL,101,3,NOW() + INTERVAL 1 DAY,800),
(NULL,102,5,NOW() + INTERVAL 2 DAY,800)
Here we are creating 3 reservations that correspond to users 100, 101, and 102.
- Mathan(user_id=100) has a reservation for 2 at restaurant XYZ (restaurant_id=800) on 3–11–2017 at 6pm
- Tyron(user_id=101) has a reservation for 3 at restaurant XYZ (restaurant_id=800) on 3–12–2017 at 6pm
- Theepan(user_id=102) has a reservation for 5 at restaurant XYZ (restaurant_id=800) on 3–13–2017 at 6pm
The columns user_id and restaurant_id would typically be foreign key references to other table columns named user.id and restaurant.id but we are simplifying things here a bit.
Entity Class
In order to model a reservation, we need to create a class called Reservation which is annotated with @Entity marking it as a JPA entity.
We’ll store the reservation id, name of the person who made the reservation, the reservation date/time, and the size of the party.
Since our class name is in title case (Reservation) and our table name is in lower case (reservation), we’ll need to let Spring know about this by using @Table(name=”reservation”) before our class declaration.
We tell Spring the private key of our table by using the @Id annotation. The @Column annotations are for other columns we want to use. If any of the database column names differ from our instance variables, then we must explicitly specify the database column name as part of the annotation — @Column(name=”user_id”)
Hint : Avoid camel case column names in your database because Hibernate converts these into snake case.
package com.mathan.springboot.reservation;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
// Model class
@Entity
@Table(name="reservation")
public class Reservation {
@Id
private Long id;
@Column
private LocalDateTime dt;
@Column(name="user_id")
private Long userId;
@Column(name="restaurant_id")
private Long restaurantId;
public Long getRestaurantId() {
return restaurantId;
}
public void setRestaurantId(Long restaurantId) {
this.restaurantId = restaurantId;
}
// Hibernate will convert camel case column names to snake case!!!
// Don't use camelcase columns in DB
@Column(name="party_size")
private int partySize;
public Reservation() {}
public Reservation(Long id, Long userId, int partySize) {
this.id = id;
this.userId = userId;
this.partySize = partySize;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDateTime getDt() {
return dt;
}
public void setDt(LocalDateTime dt) {
this.dt = dt;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public int getPartySize() {
return partySize;
}
public void setPartySize(int partySize) {
this.partySize = partySize;
}
}
Repository Interface
In a typical Java application, we would expect to write a class that implements ReservationRepository. But since we’re using Spring Data JPA, we don’t have to worry about that. JPA will create an implementation on the fly during run-time.
All we have to do is extend the CrudRepository interface like below:
package com.mathan.springboot.reservation;
import org.springframework.data.repository.CrudRepository;public interface ReservationRepository extends CrudRepository<Reservation,String> {
}
We specify the generic parameters for the entity and ID that we are working with (Reservation and String). We will inherit several methods for saving, deleting, and finding Reservation entities.
We may also define our own methods here if we wish like findByDate() and findByLastName(). For this example, we do not need to define any additional methods.
Service Class
We now create our service class named ReservationService and annotate it with @Service.
Service classes contain the business logic and call methods in the repository layer.
package com.mathan.springboot.reservation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ReservationService {
@Autowired
private ReservationRepository reservationRepository;
// Retrieve all rows from table and populate list with objects
public List getAllReservations() {
List reservations = new ArrayList<>();
reservationRepository.findAll().forEach(reservations::add);
return reservations;
}
// Retrieves one row from table based on given id
public Reservation getReservation(Long id) {
return reservationRepository.findOne(id);
}
// Inserts row into table
public void addReservation(Reservation reservation) {
reservationRepository.save(reservation);
}
// Updates row in table
public void updateReservation(Long id, Reservation reservation) {
reservationRepository.save(reservation);
}
// Removes row from table
public void deleteReservation(Long id) {
reservationRepository.delete(id);
}
}
Controller Class
In order to handle HTTP requests, we must add a controller. This will be a Java class named ReservationController with a @RestController annotation applied to it.
Inside this class, we define methods that handle the actions for each URI by using the @RequestMapping annotation. The controller class creates an instance of class ReservationService to perform its work thanks to the @Autowired annotation.
package com.mathan.springboot.reservation;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v1")
public class ReservationController {
@Autowired
private ReservationService reservationService;
// ------------ Retrieve all reservations ------------
@RequestMapping(value = "/reservations", method = RequestMethod.GET)
public List getAllReservations() {
return reservationService.getAllReservations();
}
// ------------ Retrieve a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.GET)
public Reservation getReservation(@PathVariable String id) {
return reservationService.getReservation(id);
}
// ------------ Create a reservation ------------
@RequestMapping(value = "/reservations", method =RequestMethod.POST)
public void addReservation(@RequestBody Reservation reservation) {
reservationService.addReservation(reservation);
}
// ------------ Update a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.PUT)
public void updateReservation(@RequestBody Reservation reservation,@PathVariable String id) {
reservationService.updateReservation(id, reservation);
}
// ------------ Delete a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.DELETE)
public void deleteReservation(@PathVariable String id) {
reservationService.deleteReservation(id);
}
}
JPA Converter Class
JPA does not know how to serialize LocalDateTime objects which poses a problem for us since our entity class has one. The solution to this problem is to create a helper class that instructs JPA on conversion:
package com.mathan.springboot.reservation;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
Configuration with application.properties
In this project, we will use an application.properties file in /src/main/resources. Spring Boot reads this file on startup to determine how to connect to our database.
server.address=127.0.0.1
spring.datasource.url=jdbc:mysql://localhost:3306/AnnexDB?autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=mathan95
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
Start the Application
Let’s restart the application and see how it looks. The console output will tell us if there are any issues connecting to the database.
Test the API
We have a few options for testing the API. Some popular utilities are curl (command-line) and Postman (GUI). We can also use the browser for any GET requests.
Retrieve all reservations [GET /v1/reservations]
curl -X GET -H "Content-Type: application/json" http://localhost:8080/v1/reservations
[
{
"id": 1,
"dt": {
"dayOfMonth": 12,
"dayOfWeek": "SUNDAY",
"dayOfYear": 71,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 100,
"restaurantId": 800,
"partySize": 2
},
{
"id": 2,
"dt": {
"dayOfMonth": 13,
"dayOfWeek": "MONDAY",
"dayOfYear": 72,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 101,
"restaurantId": 800,
"partySize": 3
},
{
"id": 3,
"dt": {
"dayOfMonth": 14,
"dayOfWeek": "TUESDAY",
"dayOfYear": 73,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 102,
"restaurantId": 800,
"partySize": 5
}
]
Delete a reservation [DELETE /v1/reservations/2]
curl -X DELETE -H "Content-Type: application/json" -H "Cache-Control: no-cache""http://localhost:8080/v1/reservations/2"
Run the Executable JAR as a Linux Service
The executable jar is named reservation-microservice-0.0.1-SNAPSHOT.jar.
We can run it as a Linux System V init service like so:
# ln -s /home/ec2-user/reservation-microservice-0.0.1-SNAPSHOT.jar /etc/init.d/springApp# /etc/init.d/springApp
Usage: /etc/init.d/springApp {start|stop|force-stop|restart|force-reload|status|run}
The script supports the standard service start, stop, restart and status commands.
Writes a PID file in /var/run/springApp/springApp.pidWrites console logs to /var/log/springApp.log
Backend Done Wait for the next Blog on Connecting Spring Boot With React for building a Crud Application with MYSQL.
Follow Up for more articles …
Cheers Se you all \^/