softare development

How to Create REST APIs in Java Spring Boot

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.

What is an API?

An Application Programming Interface (API) allows two applications to communicate.

For example:

  • A React website requests user data.
  • A Spring Boot API processes the request.
  • The API returns JSON.
  • React displays the information.
React App
     │
HTTP Request
     │
     ▼
Spring Boot API
     │
Business Logic
     │
     ▼
Database

Step 1: Create a Spring Boot Project

Go to Spring Initializr and create a new project.

Choose:

  • Project: Maven
  • Language: Java
  • Spring Boot: Latest Stable
  • Packaging: Jar

Dependencies:

  • Spring Web
  • Spring Data JPA
  • MySQL Driver (or PostgreSQL)
  • Lombok (optional)
  • Validation

Generate and open it in IntelliJ or VS Code.

Project Structure

src
 └── main
      ├── java
      │      └── com.example.demo
      │             ├── controller
      │             ├── service
      │             ├── repository
      │             ├── entity
      │             └── DemoApplication.java
      │
      └── resources
             └── application.properties

Each folder has a specific responsibility.

Step 2: Configure the Database

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

Step 3: Create an Entity

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.

Step 4: Create the Repository

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:

  • save()
  • findAll()
  • findById()
  • deleteById()

Step 5: Create the Service

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

Step 6: Create the Controller

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.

Understanding the Annotations

@RestController

Marks the class as a REST controller.

@RestController
public class StudentController

Instead of returning HTML, it returns JSON.

@RequestMapping

Defines the base URL.

@RequestMapping("/students")

All routes begin with:

/students

@GetMapping

Handles GET requests.

@GetMapping

Request:

GET /students

Returns:

[
  {
    "id":1,
    "name":"John",
    "email":"john@gmail.com"
  }
]

@PostMapping

Handles POST requests.

@PostMapping

Request:

POST /students

Body:

{
    "name":"Jane",
    "email":"jane@gmail.com"
}

@RequestBody

Converts incoming JSON into a Java object.

Incoming JSON:

{
  "name":"Mike",
  "email":"mike@gmail.com"
}

Automatically becomes:

Student student

Testing the API

Use:

  • Postman
  • Insomnia
  • Bruno
  • VS Code REST Client

Example request:

GET http://localhost:8080/students

POST request:

POST http://localhost:8080/students

Body:

{
    "name":"Sarah",
    "email":"sarah@gmail.com"
}

Add Update API

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"
}

Delete API

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

Get Single Student

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

Understanding HTTP Methods

MethodPurposeExample
GETRetrieve dataGet all students
POSTCreate dataAdd student
PUTReplace/update dataUpdate student
PATCHPartially update dataUpdate only email
DELETERemove dataDelete student

Common Annotations You’ll Use

AnnotationPurpose
@RestControllerCreates REST controller
@RequestMappingBase route
@GetMappingGET endpoint
@PostMappingPOST endpoint
@PutMappingPUT endpoint
@DeleteMappingDELETE endpoint
@RequestBodyReads JSON body
@PathVariableReads URL parameter
@RequestParamReads query parameter
@AutowiredInjects dependencies (constructor injection is generally preferred)
@ServiceBusiness logic
@RepositoryDatabase layer
@EntityDatabase table
@IdPrimary key
@GeneratedValueAuto-generated ID

Example API Flow

When a client requests GET /students:

Client
   │
   ▼
GET /students
   │
   ▼
StudentController
   │
calls
   ▼
StudentService
   │
calls
   ▼
StudentRepository
   │
queries
   ▼
MySQL Database
   │
returns data
   ▼
Repository
   ▼
Service
   ▼
Controller
   ▼
JSON Response

Best Practices

  • Keep controllers thin; place business logic in services.
  • Use constructor injection instead of field injection (@Autowired) for better testability.
  • Validate input with @Valid and Jakarta Validation annotations like @NotBlank and @Email.
  • Return appropriate HTTP status codes (200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found).
  • Handle exceptions globally using @ControllerAdvice for consistent error responses.
  • Use DTOs (Data Transfer Objects) instead of exposing entity classes directly in larger applications.
  • Document your API with Swagger/OpenAPI.

Typical Spring Boot API Architecture

               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.

Recent Posts

Perks of Being a Copy-Paste Developer

Why borrowing code is a skill—when you understand what you're copying. For years, "copy-paste developer"…

2 weeks ago

Conditionally Disable an Input Field Using React Hook Form

Interactive forms rarely keep every field active all the time. Sometimes an input should only…

2 weeks ago

JavaScript Temporal API

A modern JavaScript API for working with dates, times, time zones, and calendars without the…

3 weeks ago

Node.js Under the Hood

Understanding What Happens Behind the Scenes Node.js looks simple from the outside—you write JavaScript, call…

3 weeks ago

This is How You Cultivate Negative Capability

The need for absolute certainty is the greatest disease the engineering mind faces. The moment…

3 weeks ago

You Don’t have to become the World’s Greatest Programmer.

The software industry is one of the most competitive places to build a career. Every…

3 weeks ago