Build a Java Study Task Tracker

Build a local Spring Boot API that creates, lists, and deletes study tasks.

Introduction

30 Second Summary

A study checklist becomes more useful when different apps can read it or add to it. Behind that simple experience, some code has to understand each action.

In this project, you will build a local Study Task Tracker REST API using Java with Spring Boot. The finished API accepts new study tasks from Windows PowerShell before showing the updated JSON list in your browser.

What You'll Build

Picture Practice Java appearing in your browser's JSON task list seconds after a PowerShell request.

By the end of this project, you'll have:

  • Your live JSON responses at http://localhost:8080/tasks show the current study tasks in a browser.
  • Your PowerShell create request returns a new task with its own id. A refreshed browser list proves the API stored it for the current run.
  • Your request-to-method map explains how a URL plus an HTTP method reaches Java code.
  • Secret Mission: Add a DELETE endpoint that removes a task by id.

Are there any prerequisites?

You need a computer running Windows 10 or Windows 11 with internet access.

No prior Java experience is required because the setup is included.

Before We Start

This is your moment to commit to the Study Task Tracker API before the hands-on work begins. Connecting the project to your own goals gives every Java concept a clear purpose.

Set Up Java and Launch the Empty App

Your Study Task Tracker needs a working Java toolchain before Spring Boot can listen for web requests. It also needs a development environment that can generate the project structure.

This step installs Microsoft Build of OpenJDK plus IntelliJ IDEA. You will run the empty server so its root error reveals the missing endpoint.

In this step, get ready to:
  • Prepare the matching Java 25 toolchain for Windows.
  • Install IntelliJ IDEA 2026.2.1 free core.
  • Launch a Spring Boot project on port 8080.
Check your processor and install Java

Microsoft publishes separate Windows installers for x64 processors and ARM64 processors. Checking your architecture first prevents Windows from receiving an incompatible installer.

  • Select Start in the Windows taskbar.
  • Select Settings.
  • Select System.
  • Select About.
  • Find System type under Device specifications.

The system type includes either x64-based processor or ARM64-based processor. This value determines which OpenJDK installer Windows can run.

  • Record your processor architecture here: x64 or ARM64.

A JDK contains the tools required to compile Java code. Windows PowerShell can reveal whether the required version is already active.

  • Press the Windows key to open the search bar.
  • Type Windows PowerShell.
  • Press Enter to open Windows PowerShell.
  • Check the active Java version by running this command:
java -version

What Does This Command Check?

The java -version command asks the active Java runtime to identify itself. Its result shows whether Windows can find Java through PATH.

  • Choose the tab that matches the result in Windows PowerShell.

✔️ I see the required version

You should see 25.0.4.1 in the version output. Your Java toolchain is ready for the project.

ⓧ I see an older version

Windows is finding an older Java installation. Install OpenJDK 25.0.4.1 so the project uses the required LTS release.

Which Installer Matches Your PC?

  • The microsoft-jdk-25.0.4.1-windows-x64.exe installer matches x64 processors.
  • The microsoft-jdk-25.0.4.1-windows-aarch64.exe installer matches ARM64 processors.
  • Open the Microsoft Build of OpenJDK download page.
  • Download the Windows EXE that matches x64 or ARM64.
  • Double-click the downloaded EXE in your browser downloads list.
  • Approve the Windows permission request if it appears.
  • Complete the graphical installer with its default settings.
  • Close the current Windows PowerShell window.
  • Press the Windows key to open a fresh search.
  • Type Windows PowerShell.
  • Press Enter to open a fresh Windows PowerShell window.
  • Confirm the updated Java version by running this command:
java -version

What Confirms the Update?

The new output should include 25.0.4.1. A fresh PowerShell window picks up the updated PATH value from the installer.

That update gives your project the Java version required by Spring Boot 4.1.1.

ⓧ Command not found

Windows cannot find Java because a JDK is not installed yet. The native installer adds the JDK tools to PATH.

Which Installer Matches Your PC?

  • The microsoft-jdk-25.0.4.1-windows-x64.exe installer matches x64 processors.
  • The microsoft-jdk-25.0.4.1-windows-aarch64.exe installer matches ARM64 processors.
  • Open the Microsoft Build of OpenJDK download page.
  • Download the Windows EXE that matches x64 or ARM64.
  • Double-click the downloaded EXE in your browser downloads list.
  • Approve the Windows permission request if it appears.
  • Complete the graphical installer with its default settings.
  • Close the current Windows PowerShell window.
  • Press the Windows key to open a fresh search.
  • Type Windows PowerShell.
  • Press Enter to open a fresh Windows PowerShell window.
  • Confirm the Java installation by running this command:
java -version

What Confirms the Installation?

The output should include 25.0.4.1. This confirms that Windows can find the installed Java runtime.

Java is now available to every project you run from IntelliJ IDEA.

Still Unable to Find Java?

Close every Windows PowerShell window after the installer finishes. A terminal that stayed open can keep the old PATH value.

Confirm that the installer architecture matches the processor value you recorded. An x64 installer does not match an ARM64 processor.

Share the output you see through Help me troubleshoot my Java installation.

Install IntelliJ IDEA

IntelliJ IDEA combines the project wizard with Java editing tools. Its free core is enough to generate this Spring Boot project.

  • Choose the tab that matches the IntelliJ IDEA installation on your computer.

✔️ I have the required version

  • Press the Windows key to open the search bar.
  • Type IntelliJ IDEA.
  • Press Enter to open IntelliJ IDEA.
  • Continue with the free core option if a trial prompt appears.

The IntelliJ IDEA welcome window confirms that the development environment is ready.

ⓧ I have an older version

An older IntelliJ IDEA installation needs an update before you create the project. The target release for this project is 2026.2.1.

  • Open the IntelliJ IDEA 2026.2.1 release page.
  • Download the Windows EXE for version 2026.2.1.
  • Close the older IntelliJ IDEA window.
  • Double-click the downloaded EXE in your browser downloads list.
  • Approve the Windows permission request if it appears.
  • Complete the installation wizard.
  • Press the Windows key to open the search bar.
  • Type IntelliJ IDEA.
  • Press Enter to open the updated application.
  • Continue with the free core option if a trial prompt appears.

You now have IntelliJ IDEA 2026.2.1 ready to create the project.

ⓧ It is not installed yet

The standalone Windows EXE installs IntelliJ IDEA through a graphical wizard. Its core Java features remain free.

  • Open the IntelliJ IDEA 2026.2.1 release page.
  • Download the Windows EXE for version 2026.2.1.
  • Double-click the downloaded EXE in your browser downloads list.
  • Approve the Windows permission request if it appears.
  • Complete the installation wizard.
  • Press the Windows key to open the search bar.
  • Type IntelliJ IDEA.
  • Press Enter to open IntelliJ IDEA.
  • Continue with the free core option if a trial prompt appears.

Your Java development environment is installed. The welcome window is the launch point for the new project.

IntelliJ IDEA Does Not Open?

Confirm that the Windows installer finished before searching for the application. Restart Windows if the new installation does not appear in search.

Describe the screen or message you see through Help me troubleshoot the IntelliJ IDEA installation.

Create the Spring Boot project

The project wizard uses Spring Initializr to generate a working application. It also includes the Maven Wrapper so you do not need a separate Maven installation.

  • Click File in IntelliJ IDEA.
  • Select New.
  • Select Project.
  • Select Spring Boot from the project types.
  • Enter study-task-api as the project name.
  • Choose your Desktop as the project location.
  • Choose Java as the language.
  • Choose Maven as the build system.

Why Spring Boot for This Project?

A plain Java web server requires manual request handling plus server wiring. Spring Boot configures the web foundation so you can focus on the Java methods that receive requests.

Spring MVC fits this local synchronous API. Reactive programming would add concepts that this tracker does not need.

  • Select the installed JDK 25 as the project SDK.
  • Choose Java 25 as the Java version.
  • Choose JAR as the packaging type.
  • Choose Spring Boot 4.1.1 as the framework version.
  • Enter com.example as the group.
  • Enter study-task-api as the artifact.
  • Enter com.example.studytaskapi as the generated package.
  • Enter Study Task Tracker API as the description.

What Do These Settings Define?

The group plus artifact identify the Maven project. The package keeps your Java classes together under com.example.studytaskapi.

JAR packaging produces a runnable application. Spring Boot manages the matching library versions through pom.xml.

  • Continue to the dependency selection screen.
  • Select the Spring Web dependency.
  • Click Create to generate the project.

The first dependency download can take a few minutes while Maven fills its local cache. IntelliJ IDEA shows background progress while the Maven Wrapper prepares the project.

  • Wait for IntelliJ IDEA to finish loading the Maven project.
  • Confirm that pom.xml appears in the Project tool window.
  • Expand src/main/java/com/example/studytaskapi in the Project tool window.
  • Confirm that StudyTaskApiApplication.java appears in that package.
  • Confirm that mvnw appears in the project.
  • Confirm that mvnw.cmd appears in the project.

The generated main method is the Java entry point. It asks Spring Boot to start the application.

  • Open StudyTaskApiApplication.java from the Project tool window.

Before you start the application, do you expect the root URL to show a task or an error?

  • Press Shift+F10 to run StudyTaskApiApplication.

The Run window should include a Started StudyTaskApiApplication message. This confirms that the embedded server is listening on port 8080.

Application Does Not Start?

Check that the selected project SDK is JDK 25. An older SDK can prevent this Spring Boot version from starting correctly.

Wait for Maven dependency downloads to finish before running the class again. A missing dependency can stop the generated application during startup.

Share the final lines from the Run window through Help me debug the Spring Boot startup.

The server is running without any controller for the root URL. Microsoft Edge gives you a visible check of that missing route.

Before you open the root URL, what response do you think an application with no root resource returns?

  • Press the Windows key to open the search bar.
  • Type Microsoft Edge.
  • Press Enter to open Microsoft Edge.
  • Enter http://localhost:8080/ in the address bar.
  • Press Enter to request the root URL.

You should see an error because the project has no root resource. That result proves the server received the browser request.

Why Is This Error Useful?

Spring Boot is running correctly on port 8080. The error identifies the exact missing piece: your application has no controller method for the root path.

The next step adds a task route that returns real JSON.

Browser Cannot Reach the App?

Return to the IntelliJ IDEA Run window. Confirm that the application process is still active.

Check that the browser address uses port 8080. A different port sends the request somewhere else.

Describe the browser result through Help me troubleshoot the local server connection.

✔️ Awesome, I've got everything!

Your generated project files are in place. Keep the application running while you compare the empty project with the first endpoint you add next.

ⓧ I'd like to double check the full code

Compare these generated files with your project. They represent the exact application state before any task endpoint exists.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.1.1</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>study-task-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>study-task-api</name>
    <description>Study Task Tracker API</description>

    <properties>
        <java.version>25</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

What Does This File Configure?

The parent sets Spring Boot to 4.1.1. The properties section sets Java to 25.

The WebMVC starter provides the web foundation. The Maven plugin supports running plus packaging the application.

package com.example.studytaskapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StudyTaskApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(StudyTaskApiApplication.class, args);
    }
}

What Does This Class Do?

The @SpringBootApplication annotation enables configuration plus component scanning. The main method starts the application through SpringApplication.run().

Your Java toolchain is ready. Next, you will give the running server its first study task endpoint.

Serve Your First Study Task

Your Spring Boot server is already running. The empty root page proved that the tracker has no useful route.

A Java record will hold one task. A REST controller will connect an HTTP GET request to a Java method.

Spring Boot then serializes the returned record as JSON. The /tasks URL becomes the tracker's first useful route.

In this step, get ready to:
  • Model a study task with a Java record.
  • Expose the record through a GET controller method.
  • Verify the task response in Microsoft Edge.
Model a study task

A record is a compact Java class for storing data. Its three components define the values that every study task carries.

  • Expand src/main/java/com/example/studytaskapi in IntelliJ IDEA's project file tree.
  • Use the new Java file option to add StudyTask.java inside com.example.studytaskapi.
  • Replace the contents of StudyTask.java with this code:
package com.example.studytaskapi;

public record StudyTask(long id, String title, boolean completed) {
}

What does this code do?

  • The StudyTask record groups the data for one study task.
  • The id component stores the task's numeric identifier.
  • The title component stores the task description.
  • The completed component records whether the task is finished.
  • Save StudyTask.java.
  • Restart the application by pressing Shift+F10.

The Run window should show Started StudyTaskApiApplication again. This proves that Java compiled the new record.

Application Fails to Restart?

  • Confirm that StudyTask.java sits inside src/main/java/com/example/studytaskapi.
  • Compare the package line with package com.example.studytaskapi;.

Help me find the Java compilation problem.

Map GET requests to Java

The controller connects web requests to the Java code that creates a response.

  • Return to com.example.studytaskapi in IntelliJ IDEA's project file tree.
  • Use the new Java file option to add TaskController.java inside the package.
  • Replace the contents of TaskController.java with this code:
package com.example.studytaskapi;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @GetMapping
    public StudyTask getTask() {
        return new StudyTask(1, "Run the first Java web app", false);
    }
}

How Does the Controller Work?

  • The @RestController annotation makes the class available for web requests.
  • The @RequestMapping("/tasks") annotation gives every handler in the class the same base path.
  • The @GetMapping annotation sends GET requests to getTask().
  • The getTask() method returns one StudyTask object.
  • Save TaskController.java.
  • Restart the application by pressing Shift+F10.

The Run window should show Started StudyTaskApiApplication. The new controller has compiled successfully.

Controller Fails to Compile?

  • Confirm that TaskController.java uses the same com.example.studytaskapi package as StudyTask.java.
  • Check that each annotation import matches the code above.
  • Check that the returned class name is spelled StudyTask.

Help me debug the controller compilation problem.

Inspect the JSON response

The running server now has a route at /tasks.

Before you load the route, do you expect the browser to show the same root error or a task response?

  • Return to Microsoft Edge from earlier.
  • Enter http://localhost:8080/tasks in the address bar.
  • Press Enter.

You'll see a JSON object with id set to 1. Its title is Run the first Java web app.

The completed value is false. Your first endpoint is live and returning structured task data.

How Does the Request Reach Java?

  • Microsoft Edge sends a GET request to /tasks.
  • The @RequestMapping("/tasks") annotation selects TaskController.
  • The @GetMapping annotation selects getTask().
  • Spring Boot converts the returned StudyTask object into JSON.

✔️ Awesome, I've got everything!

Your two new Java files are saved. The application is running with its first task endpoint.

ⓧ I'd like to double check the full code

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.1.1</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>study-task-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>study-task-api</name>
    <description>Study Task Tracker API</description>

    <properties>
        <java.version>25</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
package com.example.studytaskapi;

public record StudyTask(long id, String title, boolean completed) {
}
package com.example.studytaskapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StudyTaskApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(StudyTaskApiApplication.class, args);
    }
}
package com.example.studytaskapi;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @GetMapping
    public StudyTask getTask() {
        return new StudyTask(1, "Run the first Java web app", false);
    }
}

Your Java code now answers a browser request with a study task. Next, you'll grow this single response into a task list and test how the GET-only controller handles a write request.

Return a Task List and Hit the Write Barrier

Your Spring Boot API now returns one study task from /tasks. That response proves a URL can reach your Java controller.

One hard-coded object cannot represent a useful tracker. You will return two tasks as JSON before testing what happens when a client tries to add one.

In this step, get ready to:
  • Replace the single task response with a fixed two-task list.
  • Verify the two-task JSON array in Microsoft Edge.
  • Send a POST request that exposes the controller's write barrier.
Return two fixed tasks

Java's List type groups several objects under one return value. List.of(...) creates the fixed collection that your read-only endpoint needs for this test.

  • Select TaskController.java in IntelliJ IDEA's Project panel.
  • Select all existing code with Ctrl+A.
  • Replace the selected controller code by pasting this version:
package com.example.studytaskapi;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @GetMapping
    public List<StudyTask> getTasks() {
        return List.of(
                new StudyTask(1, "Run the first Java web app", false),
                new StudyTask(2, "Learn how GET endpoints work", false)
        );
    }
}

What Does This Controller Change?

  • The java.util.List import makes Java's collection type available to the controller.
  • The renamed getTasks() method returns a List<StudyTask> instead of one task.
  • The List.of(...) call creates two fixed study tasks for every GET response.
  • Spring Boot converts the returned list into a JSON array for the browser.
  • Save TaskController.java with Ctrl+S.
  • Restart the application with the circular rerun control in IntelliJ IDEA's Run window.

The Run window should show a message containing Started StudyTaskApiApplication. This confirms that the updated controller compiled successfully.

Does the Application Fail to Restart?

Check that import java.util.List; appears below the package declaration. Confirm that every parenthesis and brace matches the reference code.

If IntelliJ IDEA highlights getTasks(), make sure the previous getTask() method was fully replaced.

Help me diagnose why the updated TaskController does not compile.

Inspect the JSON array

A list response appears as an array in JSON. Each task remains a separate object inside that array.

Before you refresh, predict whether the response will contain one object or an array of objects.

  • Switch back to Microsoft Edge from the previous step.
  • Reload http://localhost:8080/tasks.

You will see a JSON array containing two task objects. The first object has id 1 with the title Run the first Java web app.

The second object has id 2 with the title Learn how GET endpoints work. Both objects show completed as false.

That is a real step forward. Your endpoint can now deliver a collection of study tasks.

✔️ Awesome, I've got everything!

Great. Save TaskController.java before moving on.

ⓧ I'd like to double check the full code

package com.example.studytaskapi;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @GetMapping
    public List<StudyTask> getTasks() {
        return List.of(
                new StudyTask(1, "Run the first Java web app", false),
                new StudyTask(2, "Learn how GET endpoints work", false)
        );
    }
}
Send a write request

A read endpoint gives clients task data. A tracker also needs to accept a title from an HTTP request.

Before you send this request, predict whether the current controller can accept it.

  • Switch back to Windows PowerShell from the setup step.
  • Send the title Practice Java to the /tasks URL by running this command:
Invoke-RestMethod -Uri "http://localhost:8080/tasks" -Method Post -ContentType "application/json" -Body '{"title":"Practice Java"}'

The Write Barrier Is Visible

PowerShell reports HTTP 405 Method Not Allowed. This is the intended result.

Spring MVC found the URL but could not find a handler for the POST request. Your GET endpoint remains available.

Before you refresh, predict what the list contains after the rejected request.

  • Return to Microsoft Edge.
  • Refresh the http://localhost:8080/tasks page.

You will still see the same two fixed tasks. The rejected request did not change the list.

Seeing a Different Result?

Confirm that the application is still running on port 8080. Check that the PowerShell command uses the Post method.

If PowerShell cannot reach the API, return to IntelliJ IDEA and confirm that the Run window still shows the application as active.

Help me troubleshoot an unexpected result from the POST request.

You found the exact boundary holding the tracker back. Next up, you will add mutable storage and a request handler so new tasks can join the list.

Accept and Store New Tasks

Your controller now returns two tasks as a JSON array. The earlier POST request reached the correct URL but ended with 405 Method Not Allowed because the API had no write handler.

Now the tracker needs state that can change during the current Spring Boot run. An ArrayList keeps the tasks in memory. A request record gives the controller a clear shape for incoming titles.

In this step, get ready to:
  • Define the JSON shape for a new task title.
  • Replace the fixed task list with mutable in-memory state.
  • Handle a POST request that creates task id 3.
Create the request record

Spring needs a Java type that matches the incoming request body. The CreateTaskRequest record carries the title while the server controls the id and completion status.

  • In IntelliJ IDEA's Project tool window, select the com.example.studytaskapi package.
  • Press Alt+Insert to open the new-item menu.
  • Select Java Class.
  • Select Record.
  • Type CreateTaskRequest as the record name.
  • Press Enter.
  • Replace all generated code in CreateTaskRequest.java by pasting the code below:
package com.example.studytaskapi;

public record CreateTaskRequest(String title) {
}

What Does This Record Capture?

The CreateTaskRequest(String title) declaration defines the data accepted from a new-task request. Java automatically provides the title() accessor used by the controller.

Keeping request data separate from StudyTask prevents callers from choosing their own id or completion status.

  • Save CreateTaskRequest.java by pressing Ctrl+S.
  • Confirm CreateTaskRequest.java appears under the com.example.studytaskapi package.

Good progress. Your API now has a dedicated input shape for new task titles.

Record Showing Errors?

  • Confirm the installed JDK remains selected as the project SDK.
  • Confirm CreateTaskRequest.java sits inside src/main/java/com/example/studytaskapi.
  • Confirm the generated class declaration was completely replaced by the record declaration.

Help me fix errors in my CreateTaskRequest record.

Move tasks into mutable state

The existing List.of(...) result creates a fixed list for each GET request. A controller field backed by ArrayList keeps one collection available to both read and write methods.

This collection lives in memory for the current application run. Restarting the application rebuilds it from the two tasks in the constructor.

  • Return to TaskController.java in IntelliJ IDEA.
  • Replace the file's contents by pasting the code below:
package com.example.studytaskapi;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    private final List<StudyTask> tasks = new ArrayList<>();
    private long nextId = 1;

    public TaskController() {
        tasks.add(new StudyTask(nextId++, "Run the first Java web app", false));
        tasks.add(new StudyTask(nextId++, "Learn how GET endpoints work", false));
    }

    @GetMapping
    public List<StudyTask> getTasks() {
        return tasks;
    }
}

How Does the Mutable State Work?

  • The tasks field holds one resizable list for the current application run.
  • The nextId field starts at 1.
  • The constructor adds two tasks while advancing nextId to 3.
  • The getTasks() method returns the stored list.
  • The request annotation imports prepare the controller for the POST handler in the next substep.
  • Save TaskController.java by pressing Ctrl+S.
  • Stop the running application from IntelliJ IDEA's Run tool window.
  • Press Shift+F10 to run StudyTaskApiApplication again.

You'll see Started StudyTaskApiApplication in the Run window.

  • Switch back to Microsoft Edge from earlier.
  • Refresh http://localhost:8080/tasks.

You'll still see the two seed tasks in the JSON array. This confirms the constructor rebuilt the mutable list.

The next available id is 3.

Application Not Starting?

  • Confirm java.util.ArrayList appears above the controller declaration.
  • Confirm both seed tasks sit inside the TaskController() constructor.
  • Confirm getTasks() returns tasks.

Help me debug my mutable TaskController.

Handle the POST request

A POST mapping connects an HTTP POST request to a Java method. The @RequestBody parameter asks Spring to convert the incoming JSON into a CreateTaskRequest object.

  • In TaskController.java, locate the closing brace of the getTasks() method.
  • Add the new method directly below getTasks() by pasting this code:
    @PostMapping
    public StudyTask addTask(@RequestBody CreateTaskRequest request) {
        StudyTask task = new StudyTask(nextId++, request.title(), false);
        tasks.add(task);
        return task;
    }

What Does the POST Handler Do?

  • The @PostMapping annotation routes POST requests for /tasks to addTask().
  • The request.title() call reads the submitted title.
  • The nextId++ expression assigns the current id before advancing the counter.
  • The tasks.add(task) call stores the new task in memory.
  • Returning task sends the created object back as JSON.
  • Save TaskController.java by pressing Ctrl+S.
  • Stop the current application from IntelliJ IDEA's Run tool window.
  • Press Shift+F10 to run StudyTaskApiApplication again.

You'll see Started StudyTaskApiApplication when the updated controller is ready.

Before you send the request, what id do you expect the API to assign to the new task?

  • Send a new task from Windows PowerShell by running this command:
Invoke-RestMethod -Uri "http://localhost:8080/tasks" -Method Post -ContentType "application/json" -Body '{"title":"Practice Java"}'

What Does This Request Prove?

The 405 Method Not Allowed response is gone. Windows PowerShell returns a task with id 3 and title Practice Java.

The returned completion value is false because the controller sets every new task as incomplete.

Before you refresh the browser, how many tasks do you expect the GET request to return?

  • Switch back to Microsoft Edge from earlier.
  • Refresh http://localhost:8080/tasks.

You'll see all three tasks in the JSON array. The third object has id 3 and title Practice Java.

You have crossed the tracker's write barrier: task 3 now survives into the next GET request for this application run.

POST Request Still Failing?

  • Confirm the application was restarted after saving TaskController.java.
  • Confirm @PostMapping appears directly above addTask().
  • Confirm the request parameter uses @RequestBody CreateTaskRequest request.

Help me debug my POST request.

✔️ Awesome, I've got everything!

Great. Double check that CreateTaskRequest.java and TaskController.java are saved before moving on.

ⓧ I'd like to double check the full code

Compare both changed files with the complete versions below.

package com.example.studytaskapi;

public record CreateTaskRequest(String title) {
}

Request File Check

This complete file contains the package declaration and the one-component request record.

package com.example.studytaskapi;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    private final List<StudyTask> tasks = new ArrayList<>();
    private long nextId = 1;

    public TaskController() {
        tasks.add(new StudyTask(nextId++, "Run the first Java web app", false));
        tasks.add(new StudyTask(nextId++, "Learn how GET endpoints work", false));
    }

    @GetMapping
    public List<StudyTask> getTasks() {
        return tasks;
    }

    @PostMapping
    public StudyTask addTask(@RequestBody CreateTaskRequest request) {
        StudyTask task = new StudyTask(nextId++, request.title(), false);
        tasks.add(task);
        return task;
    }
}

Controller File Check

This complete controller initializes two tasks and returns the mutable list. Its POST handler creates task 3 from the submitted title.

Secret mission

Delete a Task by ID

Give your Study Task Tracker a way to remove completed or unwanted work. You will connect an id in the URL to a Java method. Then you will prove that the in-memory list changes.

Clean Up Your Resources

Clean Up Your Resources

Everything in this project runs locally, so there are no ongoing cloud or service charges. Decide whether to keep the application running, pause it for later, or delete the project entirely.

Resources you used:

  • The local Spring Boot application process running through IntelliJ IDEA on port 8080.
  • The local study-task-api project folder.

Keep everything running

No action is needed. Choose this if you are still testing the API or extending its endpoints.

The application continues to use memory. Port 8080 remains occupied while the process runs.

  • Leave study-task-api running through IntelliJ IDEA for more local testing.
  • Keep the study-task-api project folder with your source code.
  • Keep Microsoft Build of OpenJDK installed for future Java projects.
  • Keep IntelliJ IDEA installed for future development.

Pause - I'll come back to this later

Shut down the running application to free up memory. Your project files remain available for later.

  • Stop the running application with the stop control in IntelliJ IDEA's Run window.
  • Confirm that the Run window no longer produces new log lines.
  • Keep the study-task-api project folder in its current location.
  • Return to StudyTaskApiApplication in IntelliJ IDEA when you are ready to resume.
  • Press Shift+F10 to run the application again.

You'll see the Run window begin logging startup activity. The message Started StudyTaskApiApplication confirms that the API is running again.

Delete - I don't want to use this again

Remove the local application and its source code so you can start fresh. Your installed development tools remain available.

Deleting the study-task-api folder is permanent. Only continue when you no longer need your code.

  • Stop the running application with the stop control in IntelliJ IDEA's Run window.
  • Confirm that the Run window no longer produces new log lines.
  • Press the Windows key to open the search bar.
  • Type File Explorer and press Enter to open it.
  • Search your computer for study-task-api using the search box.

You should see the study-task-api folder from IntelliJ IDEA in the results.

  • Right-click the matching study-task-api folder.
  • Select Delete from the menu.
  • Search your computer again for study-task-api.

You should no longer see the project folder in the search results. The local API files have now been removed.

Nice Work!

Nice Work!

That’s a wrap! You built a local Study Task Tracker API with Java 25 on Spring Boot 4.1.1. Your API now handles an in-memory task lifecycle through HTTP requests.

What you learned:

  • Built a Spring Boot REST API that routes GET requests to Java controller methods.
  • Modeled study tasks with Java records. Spring Boot returns those objects as JSON.
  • Used the 405 Method Not Allowed obstacle to identify missing POST support. You then added a controller method that accepts request bodies before storing new tasks in memory.
  • Completed the optional Secret Mission by adding a DELETE endpoint with an id path variable. The tracker now removes the matching task from its in-memory list.

Ready to quiz yourself?