How to Create a Spring Boot REST API

maddy

Maddy

Posted on November 21, 2021

How to Create a Spring Boot REST API

In my previous article, I wrote about Spring Boot Architecture. I think Spring Boot is great to create REST APIs.

If you're wondering what a REST API is, "REST" stands for * Representational State Transfer*. It's a way to communicate between applications over the HTTP.

This article will show you how to create a REST API with Spring Boot from scratch.

For project development, I've used:

  • IntelliJ IDEA 2020+
  • Maven 3+
  • MySQL Workbench 8+
  • Postman v9+

GENERATE THE PROJECT

Go to the website Spring Initializr to initialize the Spring Boot project. Add the following dependencies:

  • Spring Boot DevTools: to give us the development tools.
  • Lombok: to help us reduce boilerplate code (for example, getters and setters).
  • Spring Web: to embed Apache Tomcat and include Spring MVC.
  • Spring Data JPA: to facilitate the database layer.
  • MySQL Driver: to enable the communication between the Spring Boot application and the database.

springAPISetup.png

Click "Generate" and then import the project onto your IDE as a Maven project.

This is going to be my package structure:

package-structure.png

This is my pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.techwithmaddy</groupId>
    <artifactId>CustomerAPI</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>CustomerAPI</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <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>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </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>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.1.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
Enter fullscreen mode Exit fullscreen mode

DATABASE CONFIGURATION

Once you have created the table, let's ensure that the database is set up properly with the application to allow communication between the two.

The script below should go inside the application.properties under src/main/resources.

spring.datasource.url = jdbc:mysql://localhost:3306/customer-management-system
spring.datasource.username = root
spring.datasource.password = Connection
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

server.error.include-stacktrace = never

server.port = 8083 
Enter fullscreen mode Exit fullscreen mode

CREATE A MYSQL DATABASE TABLE

I created a database schema called "customer-management-system".

Then, you can run the query below to create a Customer table.

CREATE TABLE Customer (
    id INT NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(100),
    last_name VARCHAR(100),
    email VARCHAR(100),
    phone_number VARCHAR(255),
    PRIMARY KEY (customer)
);
Enter fullscreen mode Exit fullscreen mode

Nice! We're done with all the housekeeping. Now let's jump onto the more interesting part.

CREATE A MODEL CLASS

The model class is a class that represents a real-world object. It's responsible for storing * and *retrieving the data.

Instead, the entity class is a Java class mapped to a database table, and each field corresponds to a database column. I found an interesting discussion on Quora if you'd like to know more about the difference between a model and an entity.

In the example below, we created a Customer model and linked each Java field to the columns in the database.

NOTE:

  • The @data is a Lombok annotation that puts together getters, setters, toString method, equals, hashcode, etc.
  • The @Column must match the name of the columns in the database.
package com.techwithmaddy.CustomerAPI.model;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "Customer")
@Data
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column
    private String email;

    @Column(name = "phone_number")
    private String phoneNumber;

}

Enter fullscreen mode Exit fullscreen mode

CREATE A CUSTOMER REPOSITORY INTERFACE

The repository is an interface responsible for performing database query operations. It extends the JPA Repository, which has CRUD operations built in.

The type arguments are "Customer", the domain class that will be managed, and "Integer", which is the type of id of the domain class.

In this interface, we created a method to find a customer via email.

NOTE: @Query is a Spring Data JPA annotation used to create customized database queries.

package com.techwithmaddy.CustomerAPI.repository;

import com.techwithmaddy.CustomerAPI.model.Customer;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Integer> {

    @Query("SELECT c FROM Customer c WHERE c.email =:email")
    Customer findCustomerByEmail(String email);

}

Enter fullscreen mode Exit fullscreen mode

CREATE A SERVICE CLASS

The service class is responsible for defining the business logic of our application.

The service class communicates with the repository (and the controller, which we'll build later).

For Dependency Injection, Spring can use auto-wiring. With the annotation @Autowired, Spring will search for a class that matches the property by type, and it will automatically inject the object.

In the service class, we have two methods:

  1. saveCustomer(): this is going to save the customer into the database. The good thing about extending the CRUD repository is that it provides built-in CRUD functions(such as save() ), so we don't need to define them into the service class explicitly.

  2. getCustomerByEmail(): this is going to retrieve the customer using their email. I used the Optional to handle a possible NullPointerException (what if the email we type doesn't exist?).

package com.techwithmaddy.CustomerAPI.service;

import com.techwithmaddy.CustomerAPI.model.Customer;
import com.techwithmaddy.CustomerAPI.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
public class CustomerService {

    @Autowired
    private CustomerRepository customerRepository;

    public Customer saveCustomer(Customer savedCustomer) {
        Customer customer = new Customer();
        customer.setFirstName(savedCustomer.getFirstName());
        customer.setLastName(savedCustomer.getLastName());
        customer.setEmail(savedCustomer.getEmail());
        customer.setPhoneNumber(savedCustomer.getPhoneNumber());
        return customerRepository.save(savedCustomer);
    }

    public Optional<Customer> getCustomerByEmail(String email){
        Customer customer = customerRepository.findCustomerByEmail(email);

        return Optional.ofNullable(customer);
    }

}

Enter fullscreen mode Exit fullscreen mode

CREATE A CUSTOM EXCEPTION

Along with the service class, we also create a CustomerNotFoundException class that we're going to use to throw an Exception in case a customer doesn't exist.

package com.techwithmaddy.CustomerAPI.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class CustomerNotFoundException extends RuntimeException {
    public CustomerNotFoundException(){
        super("Customer Not Found");
    }
}

Enter fullscreen mode Exit fullscreen mode

CREATE A CONTROLLER CLASS

The controller class is responsible for handling the requests coming from the client.

This controller is where we explicitly say that this application is a REST API, thanks to the @RestController annotation. This class will handle the requests.

Then, we use the @RequestMapping, which is the parent annotation of all the other mappings.

In this class, we have two methods:

  • saveCustomer(): this method saves the information of the customer into the database. We make use of the POST request to create new data.

We write the POST request like this:

http://localhost:8083/customer/save
Enter fullscreen mode Exit fullscreen mode
  • getCustomerByEmail(): this method retrieves data from the database using the email. If the email doesn't exist, then we throw a CustomerNotFoundException.

We write the GET request like this:

http://localhost:8083/customer/retrieve
Enter fullscreen mode Exit fullscreen mode
package com.techwithmaddy.CustomerAPI.api;

import com.techwithmaddy.CustomerAPI.exception.CustomerNotFoundException;
import com.techwithmaddy.CustomerAPI.model.Customer;
import com.techwithmaddy.CustomerAPI.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

@RestController
@RequestMapping(value = "/customer")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @RequestMapping(method = {POST}, path = "/save", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Customer saveCustomer(@RequestBody Customer customer){
        return customerService.saveCustomer(customer);
    }

    @RequestMapping(method = {GET}, path = "/retrieve")
    @ResponseBody
    public Customer getCustomerByEmail(@RequestParam String email){
        return customerService.getCustomerByEmail(email).orElseThrow(CustomerNotFoundException::new);
    }

}

Enter fullscreen mode Exit fullscreen mode

POSTMAN

Now we can test the application using Postman.

Let's add a couple of customers.

Enter the body of the request:

{
    "firstName": "Steve",
    "lastName": "Austin",
    "email": "steve@austin.com",
    "phoneNumber": "01223344556"
}
Enter fullscreen mode Exit fullscreen mode

Hit SEND, and Steve Austin will be saved into the database.

steve-saved.png

Let's try again with another customer.

{
    "firstName": "Linda",
    "lastName": "Delgado",
    "email": "linda@delgado.com",
    "phoneNumber": "01345678999"
}
Enter fullscreen mode Exit fullscreen mode

Linda Delgado is also saved into our database.

linda-saved.png

On Postman, you should get a 200 OK status response.

200-status-postman.png

Now that we know how to save a customer and have a couple of customers saved in our database, we can make a GET request to retrieve a customer via email.

Let's retrieve the customer, Steve.

steve-retrieved.png

And now Linda.

linda-retrieved.png

If we try to retrieve a customer that doesn't exist, we get a 404 status code, as we should expect.

customer-not-found.png

Some tips when you create your own application:

  • Follow the recommended Spring Boot package structure.

  • Running

    mvn clean install

    helps clean the dependencies and build the project, in case you remove or rename a resource from the directory.

  • Ensure that you click on the right imports.

  • Ensure that Postman accepts

    
    to avoid
    
    ``` 415 Unsupported Media Type ```
    
    . 
    
  • Ensure that you set the database schema as default schema .

CONCLUSION

In this tutorial, you have learned how to create a Spring Boot REST API.

We have:

  • Generated a Spring Boot project.
  • Created a database.
  • Linked the application to the database.
  • Created a RestController, Service and a Repository.
  • Created a Custom Exception.
  • Used Postman to test the REST API.

In my next article, we will use this application to demonstrate how to write *tests * in Spring Boot.

If you ever want to play with the application, you can clone the Github repository here.

Thanks for reading my article, until next time! πŸ‘‹πŸΎ

πŸ’– πŸ’ͺ πŸ™… 🚩
maddy
Maddy

Posted on November 21, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related