Introduction
REST stands for REpresentational State Transfer, a standardized approach to building web services.
A REST API is an intermediary Application Programming Interface that enables two applications to communicate with each other over HTTP, much like how servers communicate to browsers.
RESTful is the most common approach for building web services because of how easy it is to learn and build.
Let's say you order something from a fast-food restaurant and the cashier requests the information needed from you to process your order. After it's been processed, they hand you the order you requested for. This transaction is a real-life example of how REST API works.
In this tutorial, we'll go over how to build a REST API in Java with Spring Boot. It'll accept POST
and GET
request payloads to view and add entries from an entity - User
.
Requirements
Initializing a Spring Boot Project
Using Spring Initializr
One easy way you can initialize a new Spring Boot project is by using Spring Initializr, which automatically generates a skeleton Spring Boot project for you:
We'll add a few dependencies here as well, as we'll want to use them in our project:
- Spring Web - To include Spring MVC and embedded Tomcat into your project
- Spring Data JPA - Java Persistence API and Hibernate
- Spring Boot DevTools - Very useful development tools
- MySQL Driver - JDBC Driver (Can be any DB you'd like to use)
Afterward, press generate. A zip file that contains the generated project will then be downloaded.
Using Spring CLI
If you have Spring CLI installed, then you can opt for using the console to build your base project using this command:
spring init --build=maven -p=jar UserDemo
Note: Spring CLI directly calls Spring Initializr to perform this operation. Both options will produce the same project.
After building your base project, download and import it to your IDE or text editor of choice. If you want to build, compile, and run your application through your IDE, make sure you import it as a Maven or Gradle project.
Upon importing, the generated base pom.xml
in your project will look like this:
<!-- Project information-->
<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>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<!-- Build information -->
All the configurations that you did will be reflected in this file. On top of that, default dependencies, your base snapshot 0.0.1-SNAPSHOT
, and the Maven build plugin are also automatically configured.
For reference, if you want to build a Gradle Project, your build.gradle
will look like this:
plugins {
id 'org.springframework.boot' version '2.3.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.howto'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-java'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
Connecting Spring Boot to the Database
Next, before we start working on the application, we'll want to set up the database. This can easily be done through Spring Data JPA, which allows us to set this connection up with just a couple of parameters.
It abstracts away everything needed to be done by the developer, and allows us to switch underlying databases if we'd like, just by changing a couple of properties.
To tell Spring how to connect to your preferred database, in your application.properties
file, you'll need to add some rudimentary information:
spring.datasource.url = jdbc:mysql://localhost:3306/user
spring.datasource.username = user
spring.datasource.password = user
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
Here, we've set the datasource.url
to our JDBC connection URL. This depends on your database. We've provided the username
and password
required to authenticate into that database, as well as set the ddl-auto
property to update
. The jpa.hibernate.ddl-auto
property directly influences the hibernate.hbm2ddl.auto
property, and essentially defines how Hibernate should handle schema tool management.
For production applications, this value is typically set to none
, as dedicated personnel conduct management. In development, it's most common to use update
, to allow the schema to be updated each time you restart the application, allowing you flexibility while working on development.
Finally, we've set the hibernate.dialect
property. Hibernate has different dialects for different databases. It can automatically set the dialect based on your configuration, though, for additional safety, it's always a good idea to specify it explicitly.
Domain Model - Creating a User Model
Now that the database connection is up and running, we can go ahead and jump into the Domain Model. This is a set of classes, or rather models, that we'll use in our application. With Hibernate, they are also called Entities, as well as annotated by the @Entity
annotation.
Each @Entity
is picked up by Hibernate, a table is created for it, fields are mapped, and it becomes a managed entity for the database you've set up.
First, let's create a simple User
entity. We'll annotate the class with @Entity
and the optional @Table
annotation to specify the name for our table.
If not set, it'll just use the same name as the class:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
// Constructor, getters and setters
}
To annotate a field as the id
of an entity, you use the @Id
annotation, and it'll be set as the auto-incrementing, primary key of the table. Additionally, you can additionally set that it's a @GeneratedValue
and set the GenerationType
to AUTO
.
This is the default setting, if you omit the @GeneratedValue
annotation. Other values you can set are IDENTITY
, SEQUENCE
and TABLE
. These warrant an article of their own on Hibernate.
Additionally, you can set @Column
annotations for each of the fields, providing a name for each of them if you'd like custom names - @Column(name = "user_id")
, would save the id
field as user_id
instead of just id
.
If you'd like to automate the generation of constructors, getters and setters and just avoid the boilerplate code in total, you can use nifty tools like Lombok.
This class (entity) is now registered with Hibernate. If we run the application, considering our ddl-auto
setting, the table will show up in your respective database, with the correct table and mappings for the data types.
Persistence Layer - Creating Repository Classes
Next, let's work on the Persistence Layer. We'll want to have a UserRepository
to perform CRUD operations on our User
entities. To do this, we'll specify an interface that extends CrudRepository
, and annotate it with @Repository
.
@Repository
is a variant of the @Component
annotation, which lets Spring know that it's a component that should be managed by the IoC container. Specifically, repositories are meant to define logic for the persistence layer.
The CrudRepository
extension accepts the entity class, as well as the id
data type it should use to query:
@Repository
public interface UserRepository extends CrudRepository<User, Long> {}
CrudRepository
declares methods like findAll()
, findOne()
, and save()
which constitute the basic CRUD functionality of a repository. You can use this UserRepository
as-is, to perform CRUD operations on User
entities now, with no further setup required.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
You can override some of this behavior, if you'd like, though, it's set up automatically to help you bootstrap some basic functionality.
Business Layer - Creating a Controller
Finally, we've gotten to the Business Layer, where we implement the actual business logic of processing information, and use the components from the Persistence Layer, alongside the Domain Model to store data.
Let's create a controller, mark it as a @RestController
, as we're creating a REST API, and add a @RequestMapping
to it. @RestController
is just a combination of @Controller
and @ResponseBody
, which means that instead of rendering pages, it'll just respond with the data we've given it. This is natural for REST APIs - returning information once an API endpoint has been hit.
If you'd like to read more about
@RequestMapping
and its Derived Variants, we've got a great article dedicated just to that topic!
Let's go ahead and make a UserController
:
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping
public List<User> findAllUsers() {
// Implement
}
@GetMapping("/{id}")
public ResponseEntity<User> findUserById(@PathVariable(value = "id") long id) {
// Implement
}
@PostMapping
public User saveUser(@Validated @RequestBody User user) {
// Implement
}
}
We've @Autowired
our UserRepository
. It's used for dependency injection, as the repository class is a dependency here.
If you'd like to read more about Core Spring Framework Annotations, check out our guide!
We've also used the @GetMapping
and @PostMapping
annotations to specify which types of HTTP requests our methods are accepting and handling. These are derived variants of the @RequestMapping
annotation, with a method = RequestMethod.METHOD
set for the respective types.
Let's start off with the implementation for the findAll()
endpoint:
@GetMapping
public List<User> findAllUsers() {
return userRepository.findAll();
}
This method just calls the userRepository
to findAll()
users, and returns the list as the response.
Next, let's implement the endpoint to get each user by their id
:
@GetMapping("/{id}")
public ResponseEntity<User> findUserById(@PathVariable(value = "id") long id) {
Optional<User> user = userRepository.findById(id);
if(user.isPresent()) {
return ResponseEntity.ok().body(user.get());
} else {
return ResponseEntity.notFound().build();
}
}
A with the given id
might not be present in the database, so we wrap the returned User
in an Optional
.
If you'd like to read more about Optional in Java 8, we've got an in-depth guide!
Then, if the user.isPresent()
, we return a 200 OK
HTTP response and set the user
instance as the body of the response. Else, we return a ResponseEntity.notFound()
.
Finally, let's make an endpoint to save users:
@PostMapping
public User saveUser(@Validated @RequestBody User user) {
return userRepository.save(user);
}
The save()
method from the user repository saves a new user if it doesn't already exist. If the user with the given id
already exists, it throws an exception. If successful, it returns the persisted user.
The @Validated
annotation is a validator for the data we provide about the user, and enforces basic validity. If the user info is not valid, the data isn't saved. Also, the @RequestBody
annotation maps the body of the POST
request sent to the endpoint to the User
instance we'd like to save.
If you'd like to read more about Getting the HTTP Body in Spring Boot, we've got you covered!
Now, it's time to run the app and test if it works.
Compile, Build, and Run
The default port that Spring Boot runs in is 8080
. If you want to change the port for whatever reason, you can set it up in your application.properties
file:
server.port = 9090
If you have an IDE like IntelliJ that has extensive support for running Spring Boot projects, then you may go ahead and run it that way.
If not, we'll be using the command line to run our project. We can run the application directly by executing ./mvnw spring-boot:run
(or ./gradlew bootRun
if you're using Gradle) on the command line from your base project folder where pom.xml
is located.
Another option is to package your application into a jar
file and run it that way.
To do this, we just have to execute ./mvnw clean package
(.gradlew build
in Gradle) and run the jar file by executing this command:
$ java -jar target/DemoUser-0.0.1-SNAPSHOT.jar
If you're using Gradle, the path to the jar file will be different:
$ java -jar build/libs/DemoUser-0.0.1-SNAPSHOT.jar
You will know when your application has successfully run if you see these audit logs at the end of your command line:
2020-11-05 13:27:05.073 INFO 21796 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2020-11-05 13:27:05.108 INFO 21796 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-05 13:27:05.121 INFO 21796 --- [ restartedMain] com.howto.DemoUser.DemoUserApplication : Started DemoUserApplication in 1.765 seconds (JVM running for 2.236)
Testing the APIs
Now that your application is up and running on http://localhost:8080/
, we can now test the endpoints to see if they work.
For the GET
requests, we can use browsers, curl
or Postman - whatever's most convenient for you.
Let's hit the http://localhost:8080/api/user
endpoint with a GET
request:
$ curl http://localhost:8080/api/user
Or, in your browser address bar, visit http://localhost:8080/api/user
, and your browser will display a JSON response:
[
{
"id": 1,
"name":"John"
},
{
"id": 2,
"name":"Jane"
},
{
"id": 3,
"name": "Juan"
}
]
We can modify this URL to include a path parameter, the id
to get a specific user. Let's send an HTTP GET request to http://localhost:8080/api/user/3
:
{
"id": 3,
"name": "Juan"
}
Finally, let's send a HTTP POST request and add a user to our database, by providing the data required in our model. The fields in the JSON payload have to match the field names in our DB/model:
$ curl --location --request POST 'http://localhost:8080/api/user' \
--header 'Content-Type: application/json' \
--data-raw '{ "id": 4, "name": "Jason" }'
The API will return 200 as a response with this as the response body of the persisted user:
{
"id": 4,
"name": "Jason"
}
Conclusion
There you have it. You've successfully built your very own Spring Boot REST API!
In this tutorial, we've built a fully functioning Spring Boot project that exposes an API to the end user. Using this API, a user can perform CRUD operations on a User
entity.
We've covered the Domain Model, Persistence Layer, as well as the Business Layer, after setting up the connection to the database and configuring the project.