Updated: July 23, 2025

In the world of modern web development, REST APIs (Representational State Transfer Application Programming Interfaces) have become the backbone of communication between client and server applications. Java Spring Boot, a powerful and widely used framework, simplifies building production-grade RESTful services with minimal boilerplate code. In this article, you’ll learn how to build a simple REST API using Java Spring Boot from scratch.

What is Spring Boot?

Spring Boot is an open-source framework that makes it easy to create stand-alone, production-grade Spring-based applications. It takes an opinionated view of the Spring platform and third-party libraries, offering defaults and auto-configuration to accelerate development.

Key features include:

  • Embedded servers (e.g., Tomcat or Jetty), so you don’t need to deploy WAR files.
  • Starter dependencies for quick setup.
  • Production-ready features like metrics, health checks, and externalized configuration.
  • Minimal configuration to get started quickly.

What is a REST API?

REST (Representational State Transfer) defines a set of architectural principles for designing networked applications. A REST API exposes resources (data and functionality) via endpoints that clients can interact with using standard HTTP methods like GET, POST, PUT, DELETE.

Typical characteristics:

  • Stateless communication
  • Uniform interface using HTTP methods
  • Resource-based URLs
  • Support for various data formats (commonly JSON)

Prerequisites

Before diving in, make sure you have the following installed:

  • Java Development Kit (JDK) 11 or higher
  • Maven or Gradle build tool (we’ll use Maven)
  • An IDE such as IntelliJ IDEA, Eclipse, or VS Code
  • Basic knowledge of Java and web development

Step 1: Initialize Your Spring Boot Project

You can create a new Spring Boot project by using the Spring Initializr.

  • Project: Maven Project
  • Language: Java
  • Spring Boot version: Choose the latest stable release (e.g., 3.x)
  • Group: com.example
  • Artifact: demo-rest-api
  • Packaging: Jar
  • Java: 11 or above

Add the following dependencies:

  • Spring Web
  • Spring Data JPA
  • H2 Database (for in-memory testing)

Click Generate to download your project zip file. Extract it and open it in your IDE.

Alternatively, you can generate the project from IDE plugins or use command-line tools.

Step 2: Understand the Project Structure

Once your project is loaded, you will see a basic structure:

src/
 +- main/
      +- java/com/example/demo_rest_api/
      |    +- DemoRestApiApplication.java
      +- resources/
           +- application.properties
           +- data.sql (optional)

The DemoRestApiApplication.java contains the main method to start your Spring Boot application.

Step 3: Define the Model

Let’s build a simple API for managing Books. Each book will have an id, title, author, and publishedDate.

Create a new package called model inside com.example.demo_rest_api:

package com.example.demo_rest_api.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import java.time.LocalDate;

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private String author;

    private LocalDate publishedDate;

    // Constructors
    public Book() {}

    public Book(String title, String author, LocalDate publishedDate) {
        this.title = title;
        this.author = author;
        this.publishedDate = publishedDate;
    }

    // Getters and setters

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public LocalDate getPublishedDate() {
        return publishedDate;
    }

    public void setPublishedDate(LocalDate publishedDate) {
        this.publishedDate = publishedDate;
    }
}

Notes:

  • We are using JPA annotations to mark this class as an entity mapped to a database table.
  • The id field is marked as primary key with auto-increment generation strategy.
  • We use LocalDate for date handling.

Step 4: Create Repository Interface

Create a new package named repository. Inside it, define an interface for CRUD operations:

package com.example.demo_rest_api.repository;

import com.example.demo_rest_api.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {

}

This interface extends Spring Data JPA’s JpaRepository, which automatically provides implementations for common database operations such as save, findAll, findById, deleteById etc.

Step 5: Build the Service Layer

Though optional for simple apps, adding a service layer is good practice for business logic separation.

Create a package named service, then add:

package com.example.demo_rest_api.service;

import com.example.demo_rest_api.model.Book;
import com.example.demo_rest_api.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class BookService {

    private final BookRepository bookRepository;

    @Autowired  // Constructor injection
    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }

    public Optional<Book> getBookById(Long id) {
        return bookRepository.findById(id);
    }

    public Book saveBook(Book book) {
        return bookRepository.save(book);
    }

    public boolean deleteBook(Long id) {
        if(bookRepository.existsById(id)){
            bookRepository.deleteById(id);
            return true;
        }
        return false;
    }

}

The service encapsulates business logic and communicates with the repository layer.

Step 6: Create REST Controller

Now create a package called controller and define your REST endpoints there.

package com.example.demo_rest_api.controller;

import com.example.demo_rest_api.model.Book;
import com.example.demo_rest_api.service.BookService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/books")
public class BookController {

   private final BookService bookService;

   @Autowired
   public BookController(BookService bookService){
       this.bookService = bookService;
   }

   // GET /api/books - Retrieve all books
   @GetMapping
   public List<Book> getAllBooks() {
       return bookService.getAllBooks();
   }

   // GET /api/books/{id} - Retrieve single book by ID
   @GetMapping("/{id}")
   public ResponseEntity<Book> getBookById(@PathVariable Long id){
       return bookService.getBookById(id)
               .map(book -> ResponseEntity.ok().body(book))
               .orElse(ResponseEntity.notFound().build());
   }

   // POST /api/books - Add new book
   @PostMapping
   @ResponseStatus(HttpStatus.CREATED)
   public Book createBook(@RequestBody Book book){
       return bookService.saveBook(book);
   }

   // DELETE /api/books/{id} - Delete book by ID
   @DeleteMapping("/{id}")
   public ResponseEntity<Void> deleteBook(@PathVariable Long id){
       boolean deleted = bookService.deleteBook(id);
       if(deleted){
           return ResponseEntity.noContent().build();
       } else{
           return ResponseEntity.notFound().build();
       }
   }
}

Explanation:

  • The class is annotated with @RestController, which combines @Controller and @ResponseBody.
  • We map requests starting with /api/books.
  • Methods handle GET (all books and single by ID), POST (create new), and DELETE (remove by ID).

Spring Boot automatically converts Java objects to JSON responses thanks to its built-in HTTP message converter.

Step 7: Configure In-Memory Database

To test our API without setting up an external database, configure H2 in-memory database.

Edit the file src/main/resources/application.properties and add:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Enable H2 console access at /h2-console endpoint (optional)
spring.h2.console.enabled=true

# Show SQL statements in logs (optional)
spring.jpa.show-sql=true

# Hibernate ddl auto mode - create tables automatically on startup 
spring.jpa.hibernate.ddl-auto=update 

This configuration sets up an in-memory database that lives only during application runtime. When you restart your app, data resets.

You can visit http://localhost:8080/h2-console after running your app to interact with the database manually.

Step 8: Run Your Application

Run the main application class DemoRestApiApplication.java. Most IDEs have a Run button or right-click > Run As > Java Application.

Alternatively, use Maven in terminal:

./mvnw spring-boot:run

Your REST API server should start on default port 8080.

Step 9: Test Your API Endpoints

You can test your API via tools like Postman, curl commands, or browser plugins.

Testing GET all books

curl -X GET http://localhost:8080/api/books

Expected result: An empty JSON array ([]) if no books are added yet.

Testing POST new book

curl -X POST http://localhost:8080/api/books \
-H "Content-Type: application/json" \
-d '{"title":"Spring Boot Guide","author":"John Doe","publishedDate":"2023-04-01"}'

Response should be HTTP status 201 Created along with created JSON object including generated ID.

Testing GET single book by ID

curl -X GET http://localhost:8080/api/books/1

Expected response should be JSON representation of the first book.

Testing DELETE a book

curl -X DELETE http://localhost:8080/api/books/1

If successful, returns status code 204 No Content.

Optional Testing UI Using Postman or Swagger UI

You can also integrate Swagger UI for interactive API documentation. This requires adding extra dependencies such as springdoc-openapi-ui but is beyond scope here.

Step 10: Improve Your API (Optional Enhancements)

Here are some ways you can evolve your API once basic CRUD works:

  1. Validation
    Add annotations like @NotNull, @Size, etc., from Jakarta Bean Validation. Use @Valid annotation in controller method parameters for automatic validation and error responses.

  2. Error Handling
    Implement global exception handlers using @ControllerAdvice to handle invalid inputs or other exceptions gracefully.

  3. DTOs (Data Transfer Objects)
    Separate entity model from request/response models by creating DTOs for better API contract control.

  4. Pagination & Sorting
    Leverage Spring Data’s paging features on repository methods to handle large datasets efficiently.

  5. Security
    Secure your endpoints with Spring Security for authentication and authorization.

  6. Documentation
    Use OpenAPI/Swagger annotations for auto-generating API docs accessible via web UI.

  7. Testing
    Write unit tests for controllers and services using libraries like JUnit and Mockito; integration tests using MockMvc.


Conclusion

Building a simple REST API with Java Spring Boot is straightforward due to its convention-over-configuration approach and robust ecosystem. You learned how to set up a project from scratch; define entities; create repositories; implement service logic; expose REST endpoints; configure an in-memory database; and test your API using HTTP requests.

This foundation serves as a launchpad into more complex RESTful services tailored to real-world requirements like security, scalability, deployment pipelines, microservices architecture, message queues, caching layers, and more. Embrace these basics today and explore deeper as you build applications that power dynamic modern software solutions!