Creating RESTful API Using Spring Boot for The First Time #WeekendBuild

Last time I wrote Java was early 2013 for my undergraduate thesis. It was a image processing program. I decided wrote in Java because during college era, Java was like main programming language that been taught. Also recently while job hunting, I saw many jobs mentioned Spring Boot as their main stack, so I wonder why not try this stack for my #WeekendBuild series?

Preparation

I am using Windows machine (Windows 11) here so it could be Windows specific

  • Simply went to spring.io and check quick start page.

  • Download and install JDK. I am using JDK 21 LTS

  • Install Extension Pack for Java in VSCode

  • Once JDK installed, check Windows Environment Variable. Need to add JAVA_HOME path from installed JDK in user variable. Check in terminal or powershel with java —version. If showed up then ready to go.

  • Install Extension Pack for Java in VSCode. Then in settings.json add this config. If not, the package name would display a red mark although it’s fine.

"java.project.sourcePaths": [""]

Generate Spring Project

It’s good to know we don’t have initialize the project from scratch, we can use spring initializr.

screenshot of spring initializr

We can define the project attributes here. My setup:

  • Gradle

  • Spring Boot default (3.3.4)

  • Metadata default (demo)

  • Packaging default (Jar)

  • Java 17

  • Spring Web as dependencies because need to build RESTful API

Generate the code and later just extract the downloaded code.

I also noticed when it opened with VSCode and the extension pack already installed, Gradle will automatically download the dependencies in background so later we don’t have to do it manually.

First Web Service API

The goal is I want to create a RESTful API that accept GET /greeting request and returned JSON response.

{
    "id": 1,
    "content": "Hello, World!"
}

To do that, I need to create resource representation class.

Spring uses https://github.com/FasterXML/jackson that automatically marshal the class into JSON

// /src/main/java/com/example/demo/Greeting.java
package com.example.demo;

public record Greeting(long id, String content) { }

Next, create a REST Controller to serve the request.

// /src/main/java/com/example/demo/GreetingController.java
package com.example.demo;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
public class GreetingController {
    private static final String template = "Hello %s!";
    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "world") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}
  • @RestController annotation to identify that this controller will handle HTTP requests as part of RESTful service.

  • @GetMapping(“/greeting“) annotation it’s like a route, to ensure HTTP request to GET /greeting will be handled by Greeting function.

  • @RequestParam() annotation for binds the value of the query string parameter name into the name parameter of the greeting() method. In this case if the value empty, default value world will be used.

  • Finally it returned resource Greeting that we previously defined.

Running The Service

Go to Terminal and run this command to run the service

./gradlew bootRun

Or we can build the project and execute the .jar file

./gradlew build
java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

My first attempt of running bootRun was failed. Later that was my mistake because my generated project was set to JDK 17 but I installed JDK 21 instead. Changing the language version in the toolchain was solved the problem.

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

After the service is running, the API now is accessible at http://localhost:8080/greeting

Tests

Lets digging more to the tests. It’s possible to test the controller using MockMvc package. In this case I want to test that Greeting controller is giving 200 HTTP Code and proper JSON format.

// src/tests/java/com/example/demo/GreetingControllerTest.java
package com.example.demo;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.http.MediaType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;

@SpringBootTest
@AutoConfigureMockMvc
public class GreetingControllerTest {
    @Autowired
    private MockMvc mvc;

    @Test
    public void getGreeting() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/greeting").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id").isNumber())
            .andExpect(jsonPath("$.content").isString())
            .andExpect(jsonPath("$.content").value("Hello world!"));
    }

    @Test
    public void getGreetingWithName() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/greeting?name=johndoe").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id").isNumber())
            .andExpect(jsonPath("$.content").isString())
            .andExpect(jsonPath("$.content").value("Hello johndoe!"));
    }
}
  • @SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with @SpringBootApplication, for instance) and use that to start a Spring application context.

  • @AutoConfigureMockMvc annotation to auto inject MockMvc as the test component. It useful to only test the function layer instead of running full server.

  • @Autowired annotation is like a automatic dependency injection for MockMvc into the class test

  • @Test annotation to tell Spring that the particular function is a test case. My first attempt I missed this annotation and when I ran the tests, the test case wouldn’t be detected.

So by the code above you will know what the expectation of the test case, right?

Running the tests could be achieved at VSCode by navigate to Testing Icon on the left and click Play icon to run the tests.

screenshot of running spring tests in VSCode

Other approach is using command ./gradlew test

screenshot of running spring test using gradlew

Code Repository

I already published the full code on this repository https://github.com/didikz/my-first-spring.

Happy building, Folks!

0
Subscribe to my newsletter

Read articles from Didik Tri Susanto directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Didik Tri Susanto
Didik Tri Susanto

Hi, I am a cat lover and software engineer from Malang, mostly doing PHP and stuff. Software Engineer Live in Malang, Indonesia Visit my resume and portfolios at didiktrisusanto.dev See you, folks!