This tutorial will guide you through building a REST API using Spring Boot 3.3.0 and Java 21. We will use Java Records to transfer data between the client and server. This tutorial is designed for beginners and covers the following topics:
- Introduction to Spring Boot for Beginners
- Introduction to REST API
- Creating a Spring Boot Project
- Creating a "Hello World" REST API
- Creating a REST API that returns a Java Bean
- Creating a REST API that returns a List as JSON
- Creating a REST API that handles Path Parameters
- Creating a REST API that handles Query Parameters
- Creating CRUD REST APIs with MySQL Database
- Creating Pagination and Sorting REST APIs
Introduction to Spring Boot
Spring Boot is an open-source Java-based framework used to create stand-alone, production-grade Spring-based 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
-
Go to Spring Initializr.
-
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
-
Click on "Generate" to download the project.
-
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
-
Create a Java class named
HelloWorldController
in thesrc/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 thehelloWorld
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
-
Create a Java Record named
Person
in thesrc/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 namedPerson
with two fields:name
(aString
) andage
(anint
). Java Records are a compact syntax for immutable data classes.
-
Create a controller named
PersonController
in thesrc/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 thegetPerson
method.getPerson
Method: Returns a newPerson
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
-
Modify the
PersonController
to include a method that returns a list ofPerson
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 thegetPersons
method.getPersons
Method: Returns a list ofPerson
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
-
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 thegetPersonByName
method.getPersonByName
Method: Returns a newPerson
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
-
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 thegetPersonByNameAndAge
method.getPersonByNameAndAge
Method: Returns a newPerson
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
- Create a MySQL database named
spring_boot_db
.
Configure Database Connection
-
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
-
Create a new package named
model
and add thePerson
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
-
Create a new package named
repository
and add thePersonRepository
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 thePersonRepository
interface extendsJpaRepository
, providing CRUD operations for thePerson
entity.
Create Service Class
-
Create a new package named
service
and add thePersonService
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 thePersonRepository
bean.
Create Controller Class
-
Create a new package named
controller
and add thePersonController
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
-
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 ofPerson
data.
getPersonsPageSorted
Method: Fetches a page of Person
data sorted by the specified field.
Add Pagination and Sorting Endpoints to the Controller Class
-
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 thegetPersonsPage
method.@GetMapping("/page/sorted")
: Maps HTTP GET requests to the/page/sorted
endpoint to thegetPersonsPageSorted
method.getPersonsPage
andgetPersonsPageSorted
Methods: Fetch pages ofPerson
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.