If you know basic Java, you can build a REST API with Spring Boot in 30 minutes. In this guide, we will create a small Task API with endpoints to list, create, update, and delete tasks. We will keep the project simple: no database, no security, and no heavy architecture. The goal is to understand the moving parts first, then you can replace the in-memory storage with a real database later.
What you will build: a working JSON API at /api/tasks
using Spring Boot, Spring Web, Java records, and standard HTTP methods.
Prerequisites
Before starting, make sure you have Java installed and an editor ready. Java 17 or
newer is recommended for modern Spring Boot projects. You should also have a terminal
available so you can run the app and test the endpoints with curl.
- Java 17 or newer
- IntelliJ IDEA, VS Code, or another Java editor
- Maven wrapper from the generated Spring Boot project
- Basic knowledge of classes, methods, and HTTP requests
Minute 0-5: Create the Spring Boot Project
Open Spring Initializr and create a project with these options:
- Project: Maven
- Language: Java
- Spring Boot: latest stable version
- Java: 17 or newer
- Dependency: Spring Web
Download the project, unzip it, and open it in your editor. The important dependency
is spring-boot-starter-web, which gives you Spring MVC, JSON support,
and an embedded Tomcat server.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Minute 5-8: Understand the Project Structure
A fresh Spring Boot project looks small because most configuration is automatic. For this tutorial, we will add three files inside the main package.
src/
├── main/
│ ├── java/com/example/demo/
│ │ ├── DemoApplication.java
│ │ ├── Task.java
│ │ └── TaskController.java
│ └── resources/
│ └── application.properties
└── test/
Keep new Java files in the same package as DemoApplication.java or in
a child package. That lets Spring Boot discover your controller automatically.
Minute 8-12: Create the Task Model
Create a file named Task.java. A Java record is perfect for a simple
API response because it is concise, immutable, and automatically serialised to JSON
by Spring Boot.
package com.example.demo;
public record Task(
Long id,
String title,
boolean completed
) {}
When a controller returns a Task, Spring Boot converts it into JSON like this:
{
"id": 1,
"title": "Learn Spring Boot",
"completed": false
}
Minute 12-22: Build the REST Controller
Now create TaskController.java. This controller stores tasks in a
Map so you can focus on REST API design without setting up a database.
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
private final Map<Long, Task> tasks = new LinkedHashMap<>();
private final AtomicLong nextId = new AtomicLong(1);
@GetMapping
public List<Task> getAllTasks() {
return new ArrayList<>(tasks.values());
}
@GetMapping("/{id}")
public Task getTaskById(@PathVariable Long id) {
Task task = tasks.get(id);
if (task == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found");
}
return task;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Task createTask(@RequestBody CreateTaskRequest request) {
Long id = nextId.getAndIncrement();
Task task = new Task(id, request.title(), false);
tasks.put(id, task);
return task;
}
@PutMapping("/{id}")
public Task updateTask(@PathVariable Long id, @RequestBody UpdateTaskRequest request) {
if (!tasks.containsKey(id)) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found");
}
Task updatedTask = new Task(id, request.title(), request.completed());
tasks.put(id, updatedTask);
return updatedTask;
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteTask(@PathVariable Long id) {
if (tasks.remove(id) == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found");
}
}
public record CreateTaskRequest(String title) {}
public record UpdateTaskRequest(String title, boolean completed) {}
}
What the Annotations Mean
The code above may look compact, but it covers the core Spring Boot REST API concepts:
@RestControllertells Spring this class returns API responses, not HTML views.@RequestMapping("/api/tasks")sets the base URL for every method.@GetMapping,@PostMapping,@PutMapping, and@DeleteMappingmap HTTP methods to Java methods.@PathVariablereads values from the URL, such as/api/tasks/1.@RequestBodyconverts incoming JSON into a Java record.@ResponseStatuscontrols the HTTP status code returned by the endpoint.
Minute 22-25: Run the Application
Start the API from the project root. The Maven wrapper means you do not need Maven installed globally.
./mvnw spring-boot:run
On Windows, use:
mvnw.cmd spring-boot:run
When the logs say Tomcat started on port 8080, your REST API is live at
http://localhost:8080.
Minute 25-30: Test the REST API
Use curl or Postman to test each endpoint. First, create a task:
curl -X POST http://localhost:8080/api/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Build my first Spring Boot API"}'
You should get a response like this:
{
"id": 1,
"title": "Build my first Spring Boot API",
"completed": false
}
List all tasks:
curl http://localhost:8080/api/tasks
Get one task by ID:
curl http://localhost:8080/api/tasks/1
Update a task:
curl -X PUT http://localhost:8080/api/tasks/1 \
-H "Content-Type: application/json" \
-d '{"title":"Finish the REST API","completed":true}'
Delete a task:
curl -X DELETE http://localhost:8080/api/tasks/1
REST API Endpoint Summary
Your Spring Boot REST API now supports the standard CRUD workflow:
GET /api/tasks- list all tasksGET /api/tasks/{id}- fetch one taskPOST /api/tasks- create a new taskPUT /api/tasks/{id}- update an existing taskDELETE /api/tasks/{id}- delete a task
Common Beginner Mistakes
- Putting the controller outside the package scanned by
@SpringBootApplication. - Forgetting
@RequestBodyon methods that accept JSON. - Using
GETfor operations that create, update, or delete data. - Expecting in-memory data to survive after restarting the application.
- Forgetting the
Content-Type: application/jsonheader during POST and PUT requests.
Key Takeaways
- Spring Boot can expose REST endpoints with very little setup.
spring-boot-starter-webgives you controllers, JSON support, and an embedded server.- Java records are a clean way to model request and response data.
- Use the correct HTTP method for each action: GET, POST, PUT, and DELETE.
- An in-memory API is great for learning, but production apps should use a real database.
What's Next?
You now have a working REST API with Spring Boot in about 30 minutes. The next step
is to replace the Map with a database using Spring Data JPA,
add validation with spring-boot-starter-validation, and secure endpoints
with Spring Security. Once you understand controllers and JSON flow,
those upgrades become much easier.
If you want a more production-focused version, read the Spring Boot REST API Java 21 production setup guide next.