Spring Boot is one of the most popular frameworks for building REST APIs in Java. It removes most of the configuration required by traditional Spring applications, allowing you to build APIs quickly.
By the end of this guide, you’ll know how to create a simple CRUD API.
An Application Programming Interface (API) allows two applications to communicate.
For example:
React App
│
HTTP Request
│
▼
Spring Boot API
│
Business Logic
│
▼
Database
Go to Spring Initializr and create a new project.
Choose:
Dependencies:
Generate and open it in IntelliJ or VS Code.
src
└── main
├── java
│ └── com.example.demo
│ ├── controller
│ ├── service
│ ├── repository
│ ├── entity
│ └── DemoApplication.java
│
└── resources
└── application.properties
Each folder has a specific responsibility.
Example for MySQL:
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Suppose we’re building a Student API.
package com.example.demo.entity;
import jakarta.persistence.*;
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// getters and setters
}
This class becomes a database table.
Repositories communicate with the database.
package com.example.demo.repository;
import com.example.demo.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository
extends JpaRepository<Student, Long> {
}
Notice there is almost no code.
Spring automatically creates methods like:
The service contains business logic.
package com.example.demo.service;
import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentService {
private final StudentRepository repository;
public StudentService(StudentRepository repository) {
this.repository = repository;
}
public List<Student> getStudents() {
return repository.findAll();
}
public Student addStudent(Student student) {
return repository.save(student);
}
}
The service sits between the controller and repository.
Controller
│
▼
Service
│
▼
Repository
│
▼
Database
Controllers receive HTTP requests.
package com.example.demo.controller;
import com.example.demo.entity.Student;
import com.example.demo.service.StudentService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/students")
public class StudentController {
private final StudentService service;
public StudentController(StudentService service) {
this.service = service;
}
@GetMapping
public List<Student> getStudents() {
return service.getStudents();
}
@PostMapping
public Student createStudent(
@RequestBody Student student) {
return service.addStudent(student);
}
}
Now your API is live.
Marks the class as a REST controller.
@RestController
public class StudentController Instead of returning HTML, it returns JSON.
Defines the base URL.
@RequestMapping("/students") All routes begin with:
/students Handles GET requests.
@GetMapping
Request:
GET /students
Returns:
[
{
"id":1,
"name":"John",
"email":"john@gmail.com"
}
]
Handles POST requests.
@PostMapping Request:
POST /students Body:
{
"name":"Jane",
"email":"jane@gmail.com"
} Converts incoming JSON into a Java object.
Incoming JSON:
{
"name":"Mike",
"email":"mike@gmail.com"
} Automatically becomes:
Student student Use:
Example request:
GET http://localhost:8080/students POST request:
POST http://localhost:8080/students Body:
{
"name":"Sarah",
"email":"sarah@gmail.com"
} Service:
public Student update(Long id, Student student){
Student existing = repository.findById(id)
.orElseThrow();
existing.setName(student.getName());
existing.setEmail(student.getEmail());
return repository.save(existing);
}
Controller:
@PutMapping("/{id}")
public Student updateStudent(
@PathVariable Long id,
@RequestBody Student student){
return service.update(id, student);
} Request:
PUT /students/1 Body:
{
"name":"James",
"email":"james@gmail.com"
} Service:
public void delete(Long id){
repository.deleteById(id);
} Controller:
@DeleteMapping("/{id}")
public void deleteStudent(
@PathVariable Long id){
service.delete(id);
} Request:
DELETE /students/1 Service:
public Student getStudent(Long id){
return repository.findById(id)
.orElseThrow();
} Controller:
@GetMapping("/{id}")
public Student getStudent(
@PathVariable Long id){
return service.getStudent(id);
} Request:
GET /students/1 | Method | Purpose | Example |
|---|---|---|
| GET | Retrieve data | Get all students |
| POST | Create data | Add student |
| PUT | Replace/update data | Update student |
| PATCH | Partially update data | Update only email |
| DELETE | Remove data | Delete student |
| Annotation | Purpose |
|---|---|
| @RestController | Creates REST controller |
| @RequestMapping | Base route |
| @GetMapping | GET endpoint |
| @PostMapping | POST endpoint |
| @PutMapping | PUT endpoint |
| @DeleteMapping | DELETE endpoint |
| @RequestBody | Reads JSON body |
| @PathVariable | Reads URL parameter |
| @RequestParam | Reads query parameter |
| @Autowired | Injects dependencies (constructor injection is generally preferred) |
| @Service | Business logic |
| @Repository | Database layer |
| @Entity | Database table |
| @Id | Primary key |
| @GeneratedValue | Auto-generated ID |
When a client requests GET /students:
Client
│
▼
GET /students
│
▼
StudentController
│
calls
▼
StudentService
│
calls
▼
StudentRepository
│
queries
▼
MySQL Database
│
returns data
▼
Repository
▼
Service
▼
Controller
▼
JSON Response
@Autowired) for better testability.@Valid and Jakarta Validation annotations like @NotBlank and @Email.200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found).@ControllerAdvice for consistent error responses. HTTP Request
│
▼
StudentController
│
▼
StudentService
│
▼
StudentRepository
│
▼
MySQL Database
│
▼
JSON Response
This layered architecture keeps responsibilities separate: the Controller handles HTTP requests and responses, the Service contains business rules, the Repository manages database access, and the Entity maps Java objects to database tables. As your application grows, you can add DTOs, validation, security (Spring Security), and API documentation without changing this overall structure.
Latest tech news and coding tips.
Why borrowing code is a skill—when you understand what you're copying. For years, "copy-paste developer"…
Interactive forms rarely keep every field active all the time. Sometimes an input should only…
A modern JavaScript API for working with dates, times, time zones, and calendars without the…
Understanding What Happens Behind the Scenes Node.js looks simple from the outside—you write JavaScript, call…
The need for absolute certainty is the greatest disease the engineering mind faces. The moment…
The software industry is one of the most competitive places to build a career. Every…