Put Request in Spring - Testing

How to BootStrap a Spring Boot Application

In this article we will show you how to bootstrap a Spring Boot application quickly by using Spring Initializr. You will learn how to create a new Spring Boot application from scratch quickly and the main advantages of using Spring Boot. With our Spring Boot application example, you will be able to understand how to create web services with Spring Boot very quickly.

1. Create project

In order to easily create a new Spring Boot project, we can choose one of these two options:

1.1 Using Spring Initializr

Spring provides a service called Spring Initializr that you can use to configure and download your initial Spring Boot project. Let’s see how!

You can access Spring Initializr here. You should be able to see a view like this one:

Spring Boot - Spring Initializr
Image Credit: Author

As you can see, you can configure your new project as you wish, we are going to create a Gradle project in Kotlin using Spring Boot 2.7.5.. There is another thing we can configure initially, some of the dependencies that we want to be included in our project. In order to do that we have to click on Add Dependencies... button on the top right-hand side.

Once we click on it we can see this pop-up window:

Spring Boot - Spring Initializr Dependencies
Image Credit: Author

You can go through all the available dependencies or search for them using the search in the top bar. We have selected a few of them and once we’ve done that, our configuration looks like this:

Spring Boot - Spring Initializr
Image Credit: Author

There’s also one option to be able to visualise the content of the project before downloading it. In order to do so, you have to click on the Explore button at the middle of the bottom of your screen.

Spring Boot - Spring Initializr Explore
Image Credit: Author

You can notice how on the left-hand side all the files and directories in the project are listed. On the right-hand side you can see the content of any of the files you select, by default it shows your build.gradle.kts file.

Let’s download it now and start looking at the code, you just have to click on the Download button as expected. If you do it from the main view, you will have to click on Generate. This will download a zip file called spring-boot-app.zip.

Unzip the downloaded file to a folder on your workspace and we’re ready to start!

1.2 Using Spring Boot CLI

Another way to create your project is by using Spring Boot CLI. We’ve already explained how to do this in a previous article, you can check how to create a Spring Boot application using Spring Boot CLI in our article “Create a Spring Boot application using Spring Boot CLI”.

2. Gradle setup

If we open our recently created project, after looking at our build.gradle.kts file, we can see how a few things have already been configured for us.

For example, Spring Initializr has added the corresponding Spring Boot starter dependency for each feature we’ve chosen to enable.

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-actuator")
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	developmentOnly("org.springframework.boot:spring-boot-devtools")
	annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
}

As you can see, we have a few starter dependencies that will import the right versions of all the dependencies we need for each feature. On top of that, as we chose to create a Kotlin project, we have some dependencies required to be able to compile Kotlin code.

Apart from the dependencies, Spring Initializr also sets the required plugins to build a Kotlin Spring Boot application, as we can see here:

plugins {
	id("org.springframework.boot") version "2.7.5"
	id("io.spring.dependency-management") version "1.0.15.RELEASE"
	kotlin("jvm") version "1.6.21"
	kotlin("plugin.spring") version "1.6.21"
}

It also automatically configures some parameters we defined the values for in Spring Initializr form:

group = "com.theboreddev"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

Finally, It also adds the correct configuration to be able to compile and run tests in our Kotlin project.

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "17"
	}
}

tasks.withType<Test> {
	useJUnitPlatform()
}

3. Application

Spring Initializr creates a SpringBootAppApplication class that will be responsible to bootstrap and start our application. It looks very simple, as simple as this:

package com.theboreddev.springbootapp

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringBootAppApplication

fun main(args: Array<String>) {
	runApplication<SpringBootAppApplication>(*args)
}

This class will start our application and also uses @SpringBootApplication annotation, which will enable auto-configuration for us and also define component scanning to be able to instantiate all the Spring components we’ve defined in our application by the use of the different annotations available.

The application is initially empty, it just contains the skeleton and the wrapper to be able to start it up. It also provides an application.properties file where you can place all your configuration properties. You can also use yaml for your configuration if you prefer so, it’s just easy as renaming the extension for this file.

Let’s look at how to use properties in our configuration file.

3.1 Configuration

One of the main advantages of Spring Boot is how easy is to manage the application’s configuration. We just have to create our properties in our configuration file and map them to any of our variables.

We are going to show an example of database configuration, to do so we will add a few properties into our application.properties file. Our database configuration looks like this:

database.type=in-memory
database.user=admin
database.pass=admin

Now that we have defined our properties, how do we map them to an object in our Spring Boot application?

We have a define a configuration bean, it will be as simple as this:

@ConfigurationProperties(prefix = "database")
@Configuration
class DatabaseConfiguration {
var type: String? = null
var user: String? = null
var pass: String? = null
}

Once we have defined our configuration bean, using it in any of our components is as easy as adding it as an argument to that component! Spring will deal with everything for us and load the properties in our bean configuration.

For this example, we are going to show a very simple in-memory customer repository that will import the properties, just to print their values initially.

@Component("inMemoryCostumersRepository")
class InMemoryCustomerRepository(val config: DatabaseConfiguration): CustomersRepository {

    private val customers = mutableMapOf<Long, Customer>()

    override fun create(customer: Customer): Either<Exception, Customer> {
        println("Our DB config is [${config.type}, ${config.user}, ${config.pass}}]")
        val created = customer.copy(id = customers.keys.size.toLong())
        customers[created.id!!] = created
        return Either.Success(created)
    }

    override fun findById(id: Long): Either<Exception, Customer> {
        return customers[id]?.let { customer ->
            Either.Success(customer)
        } ?: Either.Failure(CustomerNotFound("Could not find customer $id", id))
    }
}

You will notice how we just added our DatabaseConfiguration as a dependency in our InMemoryCustomerRepository and Spring has injected all the properties in it for us. If we call our POST endpoint we can see how this line gets printed.

Our DB configuration is [in-memory, admin, admin}]

Our configuration properties have been loaded successfully, looks pretty easy, right?

Now that we’ve seen how to load our configuration, what about testing? Does Spring anything to make our testing easy?

4. Testing

It also creates an initial test for our application to just check that the Spring context gets loaded successfully, it looks like this:

package com.theboreddev.springbootapp

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class SpringBootAppApplicationTests {

	@Test
	fun contextLoads() {
	}

}

In this test we use @SpringBootTest annotation, which is responsible for starting the application with the configuration and context loaded. This annotation makes running tests in Spring Boot very easy!

Spring also provides a RestTemplate client to be able to test our APIs easily, we can inject this dependency in our test and Spring will configure it automatically for us.

We’ll talk about testing in more depth in a future article, now let’s see how to create http endpoint in our application.

5. Web Services

Once we have the initial application bootstrap, we can now add some functionality to our application by adding some REST services.

The first thing we will need is a controller, to be able to map a given url to this controller. Normally we tend to create one controller per entity in our model. For instance, in our example we will be handling customers, so we are going to create a CustomerController.

Once we have a controller, obviously, the first functionality we need is how to create a new customer. Let’s see how!

5.1 POST endpoint

The first thing we are going to do is creating a POST endpoint to create a customer in our system. The url for this endpoint will be POST /api/customers.

You can find the specifications for a POST endpoint in HTTP/1.1 here.

Let’s see how it looks like:

@RestController
@RequestMapping("/api/customers")
class CustomerController(@Autowired val repository: CustomersRepository) {

    @PostMapping
    fun createCustomer(@RequestBody customer: Customer, uriBuilder: UriComponentsBuilder): ResponseEntity<String> {
        return when (val result = repository.create(customer)) {
            is Either.Success -> {
                ResponseEntity
                    .created(uriBuilder.path("/api/customers/{id})").buildAndExpand(result.entity().id).toUri())
                    .build()
            }
            is Either.Failure -> {
                ResponseEntity.internalServerError().build()
            }
        }
    }
}

As required by the specifications, we are returning a 201 (CREATED) when a customer is successfully created. On top of that, we include a Location header pointing to the location of the recently created resource. As you may have noticed, to be able to map a POST request to our controller, we need to use the @PostMapping annotation.

We will also need to make use of @RequestBody annotation to be able to map and parse our new customer from the JSON sent by the client.

If we start our app using SpringBootAppApplication class, and use curl to create a new customer we get the following:

curl -v -H "Content-Type: application/json" --data "{\"name\": \"John Smith\", \"age\": 41}" http://localhost:8080/api/customers  
                                              
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /api/customers HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 33
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 201
< Location: http://localhost:8080/api/customers/2)
< Content-Length: 0
< Date: Sat, 26 Nov 2022 17:11:37 GMT
<
* Connection #0 to host localhost left intact

You can see in the response how the status code is what we expected, 201. Also the Location header is present pointing to the new resource, in this case it’s located under http://localhost:8080/api/customers/2.

Another thing that you might notice is that we use an Either class. We use this to be able to handle exceptions in a cleaner and more readable way. We already talked about Arrow’s Either in our article “Kotlin Either: How to Avoid Exceptions Correctly”. In this example we are using a custom Either class with just a few simple methods to simplify things for this exercise.

We will be talking about different ways of handling error scenarios in Spring Boot in a future article, but for now, let’s focus on the main topic of this article.

5.2 GET endpoint

So now that we have created a new customer, we’d like to fetch this customer, how can we do that? We will need to create a GET endpoint to be able to fetch any existing customer by ID.

    @GetMapping("/{id}")
    fun findCustomer(@PathVariable("id") id: Long): ResponseEntity<Customer> {
        return when (val result = repository.findById(id)) {
            is Either.Success -> ResponseEntity.ok(result.entity())
            is Either.Failure -> {
                when (result.exception()) {
                    is CustomerNotFound -> ResponseEntity.notFound().build()
                    else -> ResponseEntity.internalServerError().build()
                }
            }
        }
    }

As you can see, we use @GetMapping annotation this time to be able to map a GET request. Because the url includes an ID variable, we also have to use @PathVariable annotation to tell Spring that we want to map the ID variable in our url to our id variable.

We have a findById method in our repository that will return either a customer when it was found, or a CustomerNotFound error when the customer could not be found.

You can see how readable is to use a monad to handle error scenarios.

6. Metrics

If you check our build.gradle.kts file, you will be able to find a dependency called Actuator.

Spring Actuator provides a set of libraries to instantly provide some of the required production-ready features required to run your application in production.

This includes health-checks, monitoring metrics, auditing, etc.

To enable these metrics, you just need to import the dependency mentioned above in this way if you are using Gradle:

implementation("org.springframework.boot:spring-boot-starter-actuator")

If you start your application using SpringBootAppApplication class, you should be able to hit your health-check in this way:

 curl -v http://localhost:8080/actuator/health                                                                                                                                   

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /actuator/health HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Content-Type: application/vnd.spring-boot.actuator.v3+json
< Transfer-Encoding: chunked
< Date: Sat, 26 Nov 2022 17:57:23 GMT
<
* Connection #0 to host localhost left intact
{"status":"UP"}%

You can see how this endpoint returns {"status":"UP"}, indicating that our application is running and it looks healthy.

You can check all the endpoints available in Spring Actuator here.

If you are interested in learning Spring in depth, we highly recommend the following books:

Conclusion

In this article we have seen a brief introduction to Spring Boot to show you the very basics on how to quickly create a new Spring Boot application and set it up to run some web services in just a few minutes.

We will be looking at some other areas of Spring in more depth in the near future, so please follow us to be notified when they are ready, you wouldn’t want to miss them!

That’s all from us today, hoping that you come back to reading us soon! Thanks for reading us!