Spring Boot REST API Tutorial

This tutorial will guide you through building a REST API using Java 21, Spring Boot 3+ and MySQL database. We will use Java Records to transfer data between the client and server. This tutorial is designed for beginners and covers the following topics:

  1. Introduction to Spring Boot
  2. Introduction to REST API
  3. Creating a Spring Boot Project
  4. Creating a "Hello World" REST API
  5. Creating a REST API that returns a Java Bean
  6. Creating a REST API that returns a List as JSON
  7. Creating a REST API that handles Path Parameters
  8. Creating a REST API that handles Query Parameters
  9. Creating CRUD REST APIs with MySQL Database
  10. Creating Pagination and Sorting REST APIs
Spring Boot 3 REST API Tutorial

Introduction to Spring Boot

Spring Boot is an open-source Java-based framework for creating stand-alone, production-grade Spring applications. It simplifies the development process by providing defaults for code and annotation configuration, enabling you to start coding quickly without worrying about setup details.

Key Features of Spring Boot

  • Auto-Configuration: Automatically configures your Spring application based on the dependencies you have added to the project.
  • Standalone: Creates stand-alone Spring applications with embedded servers.
  • Production-ready Features: Includes production-ready features such as metrics, health checks, and externalized configuration.
  • Convention over Configuration: Reduces the need for explicit configuration by following conventions.
  • Spring Boot Starters: Provides a set of pre-configured dependencies for various functionalities, making it easy to get started.

Introduction to REST API

A REST API (Representational State Transfer Application Programming Interface) is an architectural style for building web services that interact over HTTP. REST APIs allow different software systems to communicate and exchange data efficiently. The key principles of REST include statelessness, resource-based interactions, and standardized HTTP methods like GET, POST, PUT, and DELETE.

Key Principles of REST

  • Stateless: Each request from the client to the server must contain all the information needed to understand and process the request.
  • Resource-Based: REST treats any content as a resource, such as users, posts, or items.
  • HTTP Methods: REST uses standard HTTP methods for CRUD operations (Create, Read, Update, Delete).

Creating a Spring Boot Project

Using Spring Initializr

  1. Go to Spring Initializr.

  2. Configure the project:

    • Project: Maven Project
    • Language: Java
    • Spring Boot: 3.3.0
    • Packaging: Jar
    • Java: 21
    • Dependencies: Spring Web, Spring Data JPA, MySQL Driver
  3. Click on "Generate" to download the project.

  4. Unzip the downloaded project and open it in your favorite IDE.

Example Project Structure

spring-boot-rest-api/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/demo/
│   │   │       └── DemoApplication.java
│   │   │       └── controller/
│   │   │           └── HelloWorldController.java
│   │   │           └── PersonController.java
│   │   │       └── model/
│   │   │           └── Person.java
│   │   │       └── repository/
│   │   │           └── PersonRepository.java
│   │   │       └── service/
│   │   │           └── PersonService.java
│   │   └── resources/
│   │       ├── application.properties
│   └── test/
│       └── java/
│           └── com/example/demo/
│               └── DemoApplicationTests.java
├── mvnw
├── mvnw.cmd
├── pom.xml
└── .mvn/
    └── wrapper/
        └── maven-wrapper.properties

Step 1: Creating a "Hello World" REST API

Step-by-Step Guide

  1. Create a Java class named HelloWorldController in the src/main/java/com/example/demo/controller package.

    package com.example.demo.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloWorldController {
    
        @GetMapping("/hello")
        public String helloWorld() {
            return "Hello, World!";
        }
    }
    

Explanation

  • @RestController: Indicates that the class is a REST controller, handling web requests and returning responses directly.
  • @GetMapping("/hello"): Maps HTTP GET requests to the /hello endpoint to the helloWorld method.
  • helloWorld Method: Returns a simple "Hello, World!" message when accessed.

By running the Spring Boot application and navigating to http://localhost:8080/hello, you will see the message "Hello, World!" in your browser or Postman.

Step 2: Creating a REST API that Returns a Java Bean

Step-by-Step Guide

  1. Create a Java Record named Person in the src/main/java/com/example/demo/model package.

    package com.example.demo.model;
    
    public record Person(String name, int age) {}
    

Explanation

  • Person Record: Defines a Java Record named Person with two fields: name (a String) and age (an int). Java Records are a compact syntax for immutable data classes.
  1. Create a controller named PersonController in the src/main/java/com/example/demo/controller package.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class PersonController {
    
        @GetMapping("/person")
        public Person getPerson() {
            return new Person("John Doe", 30);
        }
    }
    

Explanation

  • @GetMapping("/person"): Maps HTTP GET requests to the /person endpoint to the getPerson method.
  • getPerson Method: Returns a new Person object with the name "John Doe" and age 30 when accessed.

By running the Spring Boot application and navigating to http://localhost:8080/person, you will see the JSON representation of the Person object: {"name":"John Doe","age":30}.

Step 3: Creating a REST API that Returns a List as JSON

Step-by-Step Guide

  1. Modify the PersonController to include a method that returns a list of Person objects.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class PersonController {
    
        @GetMapping("/person")
        public Person getPerson() {
            return new Person("John Doe", 30);
        }
    
        @GetMapping("/persons")
        public List<Person> getPersons() {
            return List.of(
                new Person("John Doe", 30),
                new Person("Jane Doe", 25)
            );
        }
    }
    

Explanation

  • @GetMapping("/persons"): Maps HTTP GET requests to the /persons endpoint to the getPersons method.
  • getPersons Method: Returns a list of Person objects.

By running the Spring Boot application and navigating to http://localhost:8080/persons, you will see the JSON representation of the list of Person objects: [{"name":"John Doe","age":30},{"name":"Jane Doe","age":25}].

Step 4: Creating a REST API that Handles Path Parameters

Step-by-Step Guide

  1. Modify the PersonController to include a method that handles path parameters.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class PersonController {
    
        @GetMapping("/person")
        public Person getPerson() {
            return new Person("John Doe", 30);
        }
    
        @GetMapping("/persons")
        public List<Person> getPersons() {
            return List.of(
                new Person("John Doe", 30),
                new Person("Jane Doe", 25)
            );
        }
    
        @GetMapping("/person/{name}")
        public Person getPersonByName(@PathVariable String name) {
            return new Person(name, 30); // For simplicity, we are returning a static age.
        }
    }
    

Explanation

  • @GetMapping("/person/{name}"): Maps HTTP GET requests to the /person/{name} endpoint to the getPersonByName method.
  • getPersonByName Method: Returns a new Person object with the name provided in the path parameter and a static age of 30.

By running the Spring Boot application and navigating to http://localhost:8080/person/John, you will see the JSON representation of the Person object: {"name":"John","age":30}.

Step 5: Creating a REST API that Handles Query Parameters

Step-by-Step Guide

  1. Modify the PersonController to include a method that handles query parameters.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class PersonController {
    
        @GetMapping("/person")
        public Person getPerson() {
            return new Person("John Doe", 30);
        }
    
        @GetMapping("/persons")
        public List<Person> getPersons() {
            return List.of(
                new Person("John Doe", 30),
                new Person("Jane Doe", 25)
            );
        }
    
        @GetMapping("/person/{name}")
        public Person getPersonByName(@PathVariable String name) {
            return new Person(name, 30);
        }
    
        @GetMapping("/person")
        public Person getPersonByNameAndAge(@RequestParam String name, @RequestParam int age) {
            return new Person(name, age);
        }
    }
    

Explanation

  • @GetMapping("/person"): Maps HTTP GET requests to the /person endpoint to the getPersonByNameAndAge method.
  • getPersonByNameAndAge Method: Returns a new Person object with the name and age provided in the query parameters.

By running the Spring Boot application and navigating to http://localhost:8080/person?name=John&age=30, you will see the JSON representation of the Person object: {"name":"John","age":30}.

Step 6: Creating CRUD REST APIs with MySQL Database

Introduction

In this section, we will create a CRUD (Create, Read, Update, Delete) REST API using Spring Boot and MySQL. We will use Java Records for data transfer objects (DTOs) and demonstrate how to test the APIs using Postman.

Step-by-Step Guide

Setting Up the Database

  1. Create a MySQL database named spring_boot_db.

Configure Database Connection

  1. Update the application.properties file to configure the MySQL database connection.

    spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_db
    spring.datasource.username=root
    spring.datasource.password=root
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.show-sql=true
    

Create Entity Class

  1. Create a new package named model and add the Person entity class.

    package com.example.demo.model;
    
    import jakarta.persistence.Entity;
    import jakarta.persistence.GeneratedValue;
    import jakarta.persistence.GenerationType;
    import jakarta.persistence.Id;
    
    @Entity
    public class Person {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private int age;
    
        public Person() {}
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // Getters and setters
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

Explanation

  • @Entity: Specifies that the class is an entity and is mapped to a database table.
  • @Id: Specifies the primary key of an entity.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Provides the specification of generation strategies for the primary key values.

Create Repository Interface

  1. Create a new package named repository and add the PersonRepository interface.

    package com.example.demo.repository;
    
    import com.example.demo.model.Person;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface PersonRepository extends JpaRepository<Person, Long> {
    }
    

Explanation

  • extends JpaRepository<Person, Long>: Indicates that the PersonRepository interface extends JpaRepository, providing CRUD operations for the Person entity.

Create Service Class

  1. Create a new package named service and add the PersonService class.

    package com.example.demo.service;
    
    import com.example.demo.model.Person;
    import com.example.demo.repository.PersonRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Optional;
    
    @Service
    public class PersonService {
    
        private final PersonRepository personRepository;
    
        @Autowired
        public PersonService(PersonRepository personRepository) {
            this.personRepository = personRepository;
        }
    
        public List<Person> getAllPersons() {
            return personRepository.findAll();
        }
    
        public Optional<Person> getPersonById(Long id) {
            return personRepository.findById(id);
        }
    
        public Person createPerson(Person person) {
            return personRepository.save(person);
        }
    
        public Optional<Person> updatePerson(Long id, Person personDetails) {
            return personRepository.findById(id).map(person -> {
                person.setName(personDetails.getName());
                person.setAge(personDetails.getAge());
                return personRepository.save(person);
            });
        }
    
        public void deletePerson(Long id) {
            personRepository.deleteById(id);
        }
    }
    

Explanation

  • @Service: Indicates that the class is a service component in the Spring context.
  • public PersonService(PersonRepository personRepository): Uses constructor-based dependency injection to inject the PersonRepository bean.

Create Controller Class

  1. Create a new package named controller and add the PersonController class.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import com.example.demo.service.PersonService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    import java.util.Optional;
    
    @RestController
    @RequestMapping("/api/persons")
    public class PersonController {
    
        private final PersonService personService;
    
        @Autowired
        public PersonController(PersonService personService) {
            this.personService = personService;
        }
    
        @GetMapping
        public List<Person> getAllPersons() {
            return personService.getAllPersons();
        }
    
        @GetMapping("/{id}")
        public Optional<Person> getPersonById(@PathVariable Long id) {
            return personService.getPersonById(id);
        }
    
        @PostMapping
        public Person createPerson(@RequestBody Person person) {
            return personService.createPerson(person);
        }
    
        @PutMapping("/{id}")
        public Optional<Person> updatePerson(@PathVariable Long id, @RequestBody Person personDetails) {
            return personService.updatePerson(id, personDetails);
        }
    
        @DeleteMapping("/{id}")
        public void deletePerson(@PathVariable Long id) {
            personService.deletePerson(id);
        }
    }
    

Explanation

  • @RestController: Indicates that the class is a REST controller.
  • @RequestMapping("/api/persons"): Maps HTTP requests to the /api/persons URL.
  • CRUD Methods: Implements CRUD operations for the Person entity.

Step 7: Creating Pagination and Sorting REST APIs

Introduction

In this section, we will extend the CRUD REST API project to include pagination and sorting features. This will allow us to fetch a subset of data and sort it based on specified criteria.

Step-by-Step Guide

Add Pagination and Sorting to the Service Class

  1. Modify the PersonService class to include pagination and sorting.

    package com.example.demo.service;
    
    import com.example.demo.model.Person;
    import com.example.demo.repository.PersonRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Optional;
    
    @Service
    public class PersonService {
    
        private final PersonRepository personRepository;
    
        @Autowired
        public PersonService(PersonRepository personRepository) {
            this.personRepository = personRepository;
        }
    
        public List<Person> getAllPersons() {
            return personRepository.findAll();
        }
    
        public Optional<Person> getPersonById(Long id) {
            return personRepository.findById(id);
        }
    
        public Person createPerson(Person person) {
            return personRepository.save(person);
        }
    
        public Optional<Person> updatePerson(Long id, Person personDetails) {
            return personRepository.findById(id).map(person -> {
                person.setName(personDetails.getName());
                person.setAge(personDetails.getAge());
                return personRepository.save(person);
            });
        }
    
        public void deletePerson(Long id) {
            personRepository.deleteById(id);
        }
    
        public Page<Person> getPersonsPage(int page, int size) {
            Pageable pageable = PageRequest.of(page, size);
            return personRepository.findAll(pageable);
        }
    
        public Page<Person> getPersonsPageSorted(int page, int size, String sortBy) {
            Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
            return personRepository.findAll(pageable);
        }
    }
    

Explanation

  • getPersonsPage Method: Fetches a page of Person data.

getPersonsPageSorted Method: Fetches a page of Person data sorted by the specified field.

Add Pagination and Sorting Endpoints to the Controller Class

  1. Modify the PersonController class to include endpoints for pagination and sorting.

    package com.example.demo.controller;
    
    import com.example.demo.model.Person;
    import com.example.demo.service.PersonService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Page;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    import java.util.Optional;
    
    @RestController
    @RequestMapping("/api/persons")
    public class PersonController {
    
        private final PersonService personService;
    
        @Autowired
        public PersonController(PersonService personService) {
            this.personService = personService;
        }
    
        @GetMapping
        public List<Person> getAllPersons() {
            return personService.getAllPersons();
        }
    
        @GetMapping("/{id}")
        public Optional<Person> getPersonById(@PathVariable Long id) {
            return personService.getPersonById(id);
        }
    
        @PostMapping
        public Person createPerson(@RequestBody Person person) {
            return personService.createPerson(person);
        }
    
        @PutMapping("/{id}")
        public Optional<Person> updatePerson(@PathVariable Long id, @RequestBody Person personDetails) {
            return personService.updatePerson(id, personDetails);
        }
    
        @DeleteMapping("/{id}")
        public void deletePerson(@PathVariable Long id) {
            personService.deletePerson(id);
        }
    
        @GetMapping("/page")
        public Page<Person> getPersonsPage(@RequestParam int page, @RequestParam int size) {
            return personService.getPersonsPage(page, size);
        }
    
        @GetMapping("/page/sorted")
        public Page<Person> getPersonsPageSorted(@RequestParam int page, @RequestParam int size, @RequestParam String sortBy) {
            return personService.getPersonsPageSorted(page, size, sortBy);
        }
    }
    

Explanation

  • @GetMapping("/page"): Maps HTTP GET requests to the /page endpoint to the getPersonsPage method.
  • @GetMapping("/page/sorted"): Maps HTTP GET requests to the /page/sorted endpoint to the getPersonsPageSorted method.
  • getPersonsPage and getPersonsPageSorted Methods: Fetch pages of Person data, optionally sorted by the specified field.

By running the Spring Boot application and navigating to http://localhost:8080/api/persons/page?page=0&size=5, you can fetch the first page of Person data with 5 records per page. Similarly, by navigating to http://localhost:8080/api/persons/page/sorted?page=0&size=5&sortBy=name, you can fetch the first page of Person data sorted by name with 5 records per page.

Conclusion

This tutorial covered the basics of creating REST APIs using Spring Boot 3.3.0 and Java 21. We started with a "Hello World" REST API and gradually built more complex APIs, including handling path and query parameters, and implementing CRUD operations with MySQL. We also covered how to implement pagination and sorting in REST APIs.

By following these steps, you can build robust and scalable REST APIs with Spring Boot and Java Records for data transfer.

Comments