Getting Started with Spring Boot: Build a Task Manager App from Scratch

Spring Boot is the industry-standard framework for building modern Java applications. It dramatically reduces boilerplate code, accelerates development, and is battle-tested for microservices and monoliths alike. If you’re new to the Spring ecosystem, this post will walk you through everything you need to start building.

By the end of this guide, you’ll:

  • Understand what Spring Boot is and how it differs from Spring
  • Generate a project using Spring Initializr
  • Create a REST API with CRUD operations
  • Use an H2 in-memory database

We’ll build a simple Task Manager API with endpoints to create, read, update, and delete tasks.

What Is Spring Boot?

Spring Boot is an extension of the Spring Framework that simplifies configuration and accelerates development. It embraces convention over configuration and provides:

  • Embedded servers (no need to deploy WAR files)
  • Auto-configuration
  • Production-ready features (health checks, metrics, etc.)
  • Easy dependency management

In short, Spring Boot is Spring with batteries included.

Spring vs. Spring Boot

FeatureSpring (Core)Spring Boot
Setup effortHighMinimal (via Initializr)
Configuration styleManual (XML or Java)Auto-configured + opinionated
Server setupExternal (Tomcat, etc.)Embedded (Tomcat, Jetty, etc.)
DeploymentWARJAR (self-contained)
Production readinessManual setupBuilt-in with Actuator

Spring Boot builds on top of Spring, making it faster and easier to get started, especially for RESTful web services.


Step 1: Create the Project (Spring Initializr)

Go to https://start.spring.io and set the following:

Groupio.igventurelli
Artifacttaskmanager
ProjectMaven
LanguageJava
Spring Boot3.4.x
Packagingjar
Java17
Dependencies– Spring Web
– Spring Data JPA
– H2 Database
– Spring Boot DevTools
Spring Initializr

Generate the project and open it in your IDE.


Step 2: Understand the Generated Structure

src/
├── main/
│   ├── java/io/igventurelli/taskmanager/
│   │   └── TaskManagerApplication.java
│   └── resources/
│       ├── application.properties
  • TaskManagerApplication.java is your app’s entry point
  • application.properties stores configurations
  • You’ll be adding models, controllers, and repositories next

The main class of a Spring Boot application (in our case, TaskManagerApplication.java) serves as the entry point for your app. It is typically annotated with @SpringBootApplication, which is a convenience annotation that combines @Configuration, @EnableAutoConfiguration, and @ComponentScan. This class contains the main method, which calls SpringApplication.run(...) to bootstrap the application, initialize the Spring context, and start the embedded server. In short, it’s where everything begins.


Step 3: Create the Task Entity

package io.igventurelli.taskmanager.model;

import jakarta.persistence.*;

@Entity
public class Task {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String description;
    private boolean completed;

    // Getters and Setters
}

An entity class in Spring Boot represents a table in your database. It is annotated with @Entity and typically maps each class field to a column using annotations like @Id for the primary key and @GeneratedValue for auto-incrementing values. Entity classes are the foundation of your data model and are used by Spring Data JPA to perform CRUD operations without writing SQL manually.


Step 4: Create the Repository Interface

package io.igventurelli.taskmanager.repository;

import com.example.taskmanager.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TaskRepository extends JpaRepository<Task, Long> {}

The repository interface in Spring Boot is where you define data access operations for your entity. By extending JpaRepository (or CrudRepository), you automatically inherit common CRUD methods like save(), findById(), findAll(), and delete(). Spring Data JPA uses generics to understand which entity and primary key type you’re working with, so you can write zero boilerplate code and still interact with the database efficiently.


Step 5: Create the REST Controller

package io.igventurelli.taskmanager.controller;

import com.example.taskmanager.model.Task;
import com.example.taskmanager.repository.TaskRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/tasks")
public class TaskController {

    private final TaskRepository repository;

    public TaskController(TaskRepository repository) {
        this.repository = repository;
    }

    @GetMapping
    public List<Task> getAllTasks() {
        return repository.findAll();
    }

    @PostMapping
    public Task createTask(@RequestBody Task task) {
        return repository.save(task);
    }

    @PutMapping("/{id}")
    public ResponseEntity<?> updateTask(@PathVariable Long id, @RequestBody Task taskData) {
        return repository.findById(id)
            .map(task -> {
                task.setTitle(taskData.getTitle());
                task.setDescription(taskData.getDescription());
                task.setCompleted(taskData.isCompleted());
                repository.save(task);
                return ResponseEntity.noContent().build();
            }).orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteTask(@PathVariable Long id) {
        return repository.findById(id)
            .map(task -> {
                repository.delete(task);
                return ResponseEntity.noContent().build();
            }).orElse(ResponseEntity.notFound().build());
    }
}

The controller in a Spring Boot application handles incoming HTTP requests and maps them to the appropriate service or repository methods. Annotated with@RestController, it defines the API endpoints using annotations like @GetMapping, @PostMapping, @PutMapping, and @DeleteMapping. Controllers act as the entry point for your application’s business logic, managing the flow of data between the client and the backend. Each method in the controller typically returns a response entity or data object that is automatically serialized to JSON.

Create Request Example

Step 6: Configure the Application

Update src/main/resources/application.properties:

spring.datasource.url=jdbc:h2:mem:taskdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true

Step 7: Run Your Application

From terminal:

./mvnw spring-boot:run

Or via your IDE:

IntelliJ Run

Then import this collection in Postman to test your API:

{
  "info": {
    "name": "Task Manager API",
    "schema": "<https://schema.getpostman.com/json/collection/v2.1.0/collection.json>",
    "_postman_id": "abcd1234-ef56-7890-abcd-1234567890ab"
  },
  "item": [
    {
      "name": "Get All Tasks",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "<http://localhost:8080/api/tasks>",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8080",
          "path": ["api", "tasks"]
        }
      }
    },
    {
      "name": "Create Task",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\\n  \\"title\\": \\"Write blog post\\",\\n  \\"description\\": \\"Create a Spring Boot tutorial\\",\\n  \\"completed\\": false\\n}"
        },
        "url": {
          "raw": "<http://localhost:8080/api/tasks>",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8080",
          "path": ["api", "tasks"]
        }
      }
    },
    {
      "name": "Update Task",
      "request": {
        "method": "PUT",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\\n  \\"title\\": \\"Updated title\\",\\n  \\"description\\": \\"Updated description\\",\\n  \\"completed\\": true\\n}"
        },
        "url": {
          "raw": "<http://localhost:8080/api/tasks/1>",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8080",
          "path": ["api", "tasks", "1"]
        }
      }
    },
    {
      "name": "Delete Task",
      "request": {
        "method": "DELETE",
        "header": [],
        "url": {
          "raw": "<http://localhost:8080/api/tasks/1>",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8080",
          "path": ["api", "tasks", "1"]
        }
      }
    }
  ]
}
Postman Collection Imported

Final Thoughts

You’ve now built a real-world CRUD API with Spring Boot and H2. In under 30 minutes, you’ve seen the power of auto-configuration, embedded servers, and clean code organization that Spring Boot brings to Java development.

What’s Next?

  • Add validation using @Valid
  • Use PostgreSQL instead of H2
  • Add a service layer
  • Secure your API with Spring Security

Spring Boot is just the beginning of modern Java backend development. With this foundation, you can scale into microservices, integrate Kafka, build GraphQL endpoints, or deploy to the cloud.

▶️ Recommended watch: How to Handle Multiple Environments (dev, qa, prd) on Spring Boot applications? The BEST way!

Leave a Reply

Your email address will not be published. Required fields are marked *