How to Write Your Own Spring Boot REST Service

Learn to create a Spring Boot Rest Application from scratch.

Overview

In this article, we will write our own Spring Boot REST Service from scratch. Spring Boot’s auto-configuration comes handy when you want to concentrate on your Business rather than spending time on writing framework components and boiler plate code.

Today, we will create a Simple RESTful Service. Let’s write a Dog Service. The quickest way to get on with a Spring Boot project is to use SPRING INITIALIZR.

Create Spring Boot Project

Visit the SPRING INITIALIZR portal, create new maven or gradle project, and add Spring Web dependency. Lastly, download the project and you are good to proceed.

Dependencies

Make sure, you maven dependency section has spring-boot-starter-web dependency.

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>Or
Code language: HTML, XML (xml)

Or, if you are using Gradle, then this

implementation('org.springframework.boot:spring-boot-starter-web')
Code language: Gradle (gradle)

Optionally, we will use Lombok to help reduce some boiler plate code from our application. Lombok helps a lot. Visit Project Lombok for more information.

Application.java

Here is our starting point Application.java has the friendly public static void main method. Where we actually start a Spring Boot Application.

package com.amitph.spring.dogs; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Code language: Java (java)

At this point, you have your Spring Boot application up and running. You can confirm by running the Application.java like any other Java class and see the server being up and listening for port 8080 (configurable).

Data Provider

As this is our Spring Boot beginners tutorial, and we want to focus on the web part of the application. Thus, we will not use a real database for this application. Instead, we will create a Mock data provider component.

Mock Dog Provider has an instance level pre-populated list of Dogs. The various CRUD methods in the class, actually perform the operation on the Dogs List.

package com.amitph.spring.dogs.repo; import com.amitph.spring.dogs.model.DogDto; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; @Component public class MockDogProvider { private List<Dog> mockDogStore; public MockDogProvider() { mockDogStore = new ArrayList<>(); mockDogStore.add(Dog.of(1, "Benji", 10)); mockDogStore.add(Dog.of(2, "Baxter", 9)); mockDogStore.add(Dog.of(3, "Brinkley", 8)); mockDogStore.add(Dog.of(4, "Daisy", 10)); mockDogStore.add(Dog.of(5, "Cujo", 12)); } public List<Dog> getDogs() { return mockDogStore; } public Dog findDogById(long id) { for (Dog dog : mockDogStore) { if (dog.getId() == id) { return dog; } } return null; } public void add(DogDto dto) { mockDogStore.add(Dog.of(dto.getId(), dto.getName(), dto.getAge())); } public void delete(long id) { int idx = 0; for (; idx < mockDogStore.size(); idx++) { if (mockDogStore.get(idx).getId() == id) { break; } } mockDogStore.remove(idx); } }
Code language: Java (java)

Service Layer

Dogs service is a routine Service Layer of our web application. It just decouples controller and data provider. Because no business logic is involved, it just delegates the calls to Data Provider.

package com.amitph.spring.dogs.service; import com.amitph.spring.dogs.model.DogDto; import com.amitph.spring.dogs.repo.Dog; import com.amitph.spring.dogs.repo.MockDogProvider; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; @Component @RequiredArgsConstructor @Setter public class DogsService { @Autowired private final MockDogProvider mockDogProvider; public void add(DogDto dto) { mockDogProvider.add(dto); } public void delete(long id) { mockDogProvider.delete(id); } public List<Dog> getDogs() { return mockDogProvider.getDogs(); } public Dog getDogById(long id) { return mockDogProvider.findDogById(id); } }
Code language: Java (java)

Rest Controller

We are almost there. What we need in the end is a controller which handles the REST calls and delegate to Service.

package com.amitph.spring.dogs.web; import com.amitph.spring.dogs.model.DogDto; import com.amitph.spring.dogs.repo.Dog; import com.amitph.spring.dogs.service.DogsService; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/dogs") @RequiredArgsConstructor @Setter public class DogsController { @Autowired private final DogsService service; @GetMapping public List<Dog> getDogs() { return service.getDogs(); } @PostMapping public void postDogs(@RequestBody DogDto dto) { service.add(dto); } @GetMapping("/{id}") public Dog getById(@PathVariable(required = true) long id) { return service.getDogById(id); } @DeleteMapping("/{id}") public void delete(@PathVariable(required = true) long id) { service.delete(id); } }
Code language: Java (java)

Run the application

/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.0.RELEASE) INFO [main] com.amitph.spring.dogs.Application : No active profile set, falling back to default profiles: default INFO [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) INFO [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] INFO [main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.12 INFO [main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/aphaltankar/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] INFO [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext INFO [main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 893 ms INFO [main] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/] INFO [main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] INFO [main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] INFO [main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'formContentFilter' to: [/*] INFO [main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] INFO [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' INFO [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' INFO [main] com.amitph.spring.dogs.Application : Started Application in 1.75 seconds (JVM running for 2.44) INFO [io-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' INFO [io-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' INFO [io-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 8 ms
Code language: plaintext (plaintext)

Get All Dogs

Open the browser and execute (GET)

http://localhost:8080/dog

And below is the output

[ { "id":1, "name":"Benji", "age":10 }, { "id":2, "name":"Baxter", "age":9 }, { "id":3, "name":"Brinkley", "age":8 }, { "id":4, "name":"Daisy", "age":10 }, { "id":5, "name":"Cujo", "age":12 } ]
Code language: JSON / JSON with Comments (json)

Get Dog by Id

Let’s make a GET request

http://localhost:8080/dogs/3

And below is the output

{ "id":3, "name":"Brinkley", "age":8 }
Code language: JSON / JSON with Comments (json)

Add New Dog

This is a POST request I will use curl to make this call.

curl -X POST \ http://localhost:8080/dogs \ -H 'Content-Type: application/json' \ -H 'Postman-Token: c6813aea-146c-49cd-9eba-1370aad4bff9' \ -H 'cache-control: no-cache' \ -d '{ "id": 6, "name": "Hooch", "age": 11 }'
Code language: Bash (bash)

After this execute the GET /dogs again and you will see a new dogs has been added.

Delete a Dog

This a DELETE request using curl here.

curl -X DELETE \ http://localhost:8080/dogs/5 \ -H 'Content-Type: application/json' \ -H 'Postman-Token: b4b93304-7ee7-45c2-917b-c3bc2985a250' \ -H 'cache-control: no-cache'
Code language: Bash (bash)

After this you should see Dog with id 5 is gone from the list

Summary

So we have created our Spring Boot Rest Example. We created a Dog Service with simple Create, GET and DELETE endpoints. We have seen how easy a service is with Spring Boot where you can skip lot of crap and concentrate on your business.

In the next article we will refer to the Same Dog Service and learn Spring Rest Service Exception Handling. We will learn how spring takes care of all the crappy exceptions handling stuff lets us do more important things.

For full source code of the examples used here, please visit our Github Repository.