Introduction

In this article we will describe a simple approach to implementing a Rest API in spring boot and automate its deployment. The API is intended for only one resource i.e. User implementing the basic CRUD methods(GET, POST, PUT, PATCH, DELETE), as well as Open-API documentation powered by Swagger.

prerequisites

  • Knowledge in Java 8+

Getting Started

Ensure you have JDK and Maven installed. Head over to Spring Initializr and create your Spring Boot project.

Hit generate and extract the project to get started.

Define Your Folder Structure

├── Dockerfile
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── aws
│   │   │           └── users
│   │   │               ├── entities
│   │   │               ├── UsersApplication.java
│   │   │               └── repositories
│   │   └── resources
│   │       └── application.properties
│   └── test
│       └── java
│           └── com
│               └── aws
│                   └── users
│                       └── UsersApplicationTests.java

As defined in the structure above navigate to the base of your main package and create entities and repositories folders. It is advisable to layer your application structure, an example is as shown below. This will help in modularizing the applications and better organize your code.

├── Dockerfile
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── aws
│   │   │           └── users
│   │   │               ├── entities/
│   │   │               ├── configs/
│   │   │               ├── controllers/
│   │   │               ├── helpers/
│   │   │               ├── ... (more)
│   │   │               ├── services/
│   │   │               ├── UsersApplication.java
│   │   │               └── repositories/
│   │   └── resources
│   │       └── application.properties
│   └── test
│       └── java
│           └── com
│               └── aws
│                   └── users
│                       └── UsersApplicationTests.java

Run The application to ensure everything is working fine before we start our modifications.

mvn spring-boot:run

Set up necessary Dependencies

These can be set up manually by modifying the pom.xml

<!-- https://spring.io/projects/spring-data-jpa -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- https://spring.io/projects/spring-data-rest -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

Set Up Database

Open the application.properties and modify DSN(data source name) and user credentials as shown below, to allow us connect to the database.

spring.datasource.url=jdbc:mysql://{db_host}:{port}/{db_name}
spring.datasource.username={db_user_name}
spring.datasource.password={db_password}

Set up entities

An Entity is a definition of your data-store that directly maps on to a table in the database. Springs JPA auto generates tables from model as a default setting however this behavior can be changed using configuration as below(on the application.properties). below are some sample ddl properties

  • create - recreates tables purging all existing data
  • update - updates structure keeping data intact
  • validate - ensures that current db has desired table schema
spring.jpa.hibernate.ddl-auto=create

Create the User resource entity in the User.java file as defined below; You can use lombok to encapsulate your models instead of using traditional getters and setters to reduce boilerplate code.

package com.aws.{your_packaging}.entities;

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

import org.springframework.lang.NonNull;

@Entity(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false, unique = true)
    private String id;

    @NonNull
    @Column(name = "userId", nullable = false, unique = true)
    private String userId;

    @NonNull
    @Column(name = "name", nullable = false)
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Set up Repository / DAO (Data Access Object)

A repository acts as an interface between the business logic and business objects(entities) to allow writes, reads and updates to the database. Within the repositories folder create UserRepository.java file and modify as below.

Extending JpaRepository exposes a complete set of methods to manipulate your entities. If you prefer to be selective about the methods being exposed, copy the methods you want to expose from JpaRepository into your domain repository this will override the super methods.

package com.aws.{your_packaging}.repo;

import com.aws.{your_packaging}.entities.User;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, String> {

}

Spring data rest generates a CRUD rest resource automatically for each defined JPARepository. It is possible to override the default paths at the controller level and pass this through some self defined business logic. Spring data rest implements HATEOAS standard on the response data by default. Below are a few available configs to allow you handle your REST methods

# Exposes all public repository interfaces but considers @(Repository) `exported flag.
spring.data.rest.detection-strategy=default

# Exposes all repositories independently of type visibility and annotations.
spring.data.rest.detection-strategy=all

# Only repositories annotated with @(Repository)RestResource are exposed, unless their exported flag is set to false.
spring.data.rest.detection-strategy=annotated

# Only public repositories annotated are exposed.
spring.data.rest.detection-strategy=visibility

Documenting the API

springdoc-openapi java library is used to auto generate REST documentation for the project. This is set up by merely adding the dependencies to the pom.xml.

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>{get latest version from springdoc}</version>
</dependency>

This will generate open API specifications based on all exposed REST methods and auto defines the schema based on the defined request bodies as well as responses. The UI will by default implemented using swagger, default path can be accessed at http://{url}/swagger-ui.html.

Dockerizing the Application

For this step you need to have both docker and docker compose installed on your server or local computer.

You can find the tutorial on docker windows, docker mac, docker linux (Debian and Ubuntu) and docker compose

Dockerizing Spring boot applications is quite simple and requires minimal steps, We'll take advantage of the below sample.

# Start with a base image containing Java runtime
FROM openjdk:8-jdk-alpine

# Add Maintainer Info
LABEL maintainer="adamsokode@gmail.com"

# Add a volume pointing to /tmpe
VOLUME /tmp

# Copy all assets into the volume
ADD  .  /

# Export environment variables
RUN export $(cat .env | xargs) # export environment args 

# Build jar
RUN ./mvnw package

# Make port 8080 available to the world outside this containr
EXPOSE 8080


# Run the jar file replace jar file with your packaging
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/target/users-0.0.1-SNAPSHOT.jar"]

Deployment

For this step you need to have both docker and docker compose installed on your server or local computer.

define your docker-compose orchestration file(docker-compose.yml)

services:
  web:
    container_name: spring.testapp.application
    build: .
    ports:
      - "8080:8080"
    env_file: .env
    depends_on:
      - mysql
    networks:
      local_network:
        ipv4_address: 172.28.1.4

  mysql:
    image: mysql:latest
    container_name: spring.testapp.mysql
    volumes:
      - db_data:/var/lib/mysql:rw
    environment:
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
    ports:
      - "${DB_PORT}:${DB_PORT}"
    networks:
      local_network:
        ipv4_address: ${DB_HOST}
    env_file: .env

volumes:
  db_data:

networks:
  local_network:
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16

If all this is set up then navigate to your project folder within your specific CLI and run

docker-compose up -d --build

The above command will build and deploy the image to the defined stack in detached mode, once the container is up and running open http://{ your-ip }:{your-port} to access your application

Remarks

In this tutorial, we walked through how to build and containerise a Spring boot application using Spring data. We also created a production-ready Docker Compose file that adds. You can now test out a production setup locally.

For an actual production deployment site:

  • You may want to use a fully managed database service -- like RDS/Aurora or Cloud SQL -- rather than managing your own MySQL instance within a container.
  • Non-root user for the db

To help you manage your docker environment you can install Portainer a Docker GUI management center.

You can find the full project on this @repo

Let me know your thoughts on the comment section.

You've successfully subscribed to Decoded For Devs
Welcome back! You've successfully signed in.
Great! You've successfully signed up.
Your link has expired
Success! Your account is fully activated, you now have access to all content.