Student CRUD API
STEP 1 · GOAL

Build Your First Spring Boot CRUD API

We will create a very small Student REST API. To keep the first backend lesson easy, almost everything will live inside one controller file.

By the end: Create a Spring Boot project using start.spring.io, open it in VS Code, run it, create one StudentController.java, store students in an ArrayList, and implement GET, POST, PUT, and DELETE APIs.
Client / Postman / Flutter
↓ HTTP Request
StudentController
↓ reads or changes
ArrayList<Student>
↓ returns Java object
Spring/Jackson converts it to JSON
↓ HTTP Response
Beginner-first design

No database, repository, service layer, DTO, or advanced architecture yet. First understand how an HTTP request reaches Java code.

STEP 2 · PREPARE

Install the Required Tools

You only need Java, VS Code, and a few extensions.

Install

  • JDK 17 or newer
  • Visual Studio Code
  • Extension Pack for Java
  • Spring Boot Extension Pack
java -version
CHECK

If the command prints a Java version, your JDK is available.

STEP 3 · START.SPRING.IO

Generate the Spring Boot Project

Spring Initializr creates the starting Maven project for us.

  1. Open https://start.spring.io.
  2. Choose Maven.
  3. Choose Java.
  4. Select a stable Spring Boot version.
  5. Group: com.example
  6. Artifact: studentapi
  7. Name: studentapi
  8. Packaging: Jar
  9. Java: choose your installed version, for example 17 or 21.
  10. Add dependency: Spring Web.
  11. Click Generate.
Why Spring Web?

It gives us the tools required to create REST endpoints such as @RestController, @GetMapping, @PostMapping, and an embedded web server.

STEP 4 · VS CODE

Open the Project

Extract the downloaded ZIP and open the project folder in VS Code.

studentapi/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/example/studentapi/
│ │ │ └── StudentapiApplication.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
└── mvnw / mvnw.cmd
File Purpose
pom.xml Maven project configuration and dependencies.
StudentapiApplication.java Starts Spring Boot.
application.properties Application configuration.
STEP 5 · RUN

Start Spring Boot Once

Before writing APIs, confirm that the generated project runs.

mvn spring-boot:run
Expected result

The terminal should show that Tomcat started, normally on port 8080. Keep the application running while testing endpoints.

STEP 6 · UNDERSTAND

Understand the Generated Main Class

Spring Initializr already created the class that starts our application.

package com.example.studentapi;

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

@SpringBootApplication
public class StudentapiApplication {

    public static void main(String[] args) {
        SpringApplication.run(StudentapiApplication.class, args);
    }
}
Code Meaning
@SpringBootApplication Marks this as the main Spring Boot application and enables automatic configuration/component scanning.
main() Normal Java program entry point.
SpringApplication.run(...) Starts Spring, creates the application context, and starts the embedded server.
STEP 7 · CONTROLLER

Create Only One New Java File

Inside the same package, create StudentController.java. We are deliberately not creating separate model, service, or repository files.

src/main/java/com/example/studentapi/
├── StudentapiApplication.java
└── StudentController.java
package com.example.studentapi;

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

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

@RestController
@RequestMapping("/api/students")
public class StudentController {

}
Code Meaning
@RestController Tells Spring that this class contains REST API endpoints.
@RequestMapping("/api/students") Adds the same base URL to every endpoint in this controller.
ResponseEntity Lets us control the HTTP response status and body.
STEP 8 · DATA

Put Student and ArrayList Inside the Controller

The controller will contain the Student class and our temporary in-memory data.

private final List<Student> students = new ArrayList<>();
private int nextId = 1;

public StudentController() {
    students.add(new Student(nextId++, "Asha", 20, "CSE"));
    students.add(new Student(nextId++, "Rahul", 21, "ECE"));
}

public static class Student {
    private int id;
    private String name;
    private int age;
    private String course;

    public Student() {
    }

    public Student(int id, String name, int age, String course) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.course = course;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course = course;
    }
}
ArrayList

Acts like our temporary database while the program is running.

nextId

Generates IDs because we do not have a database doing that for us.

Why the empty constructor?

When JSON arrives in a request body, Spring uses Jackson to create a Student object. A no-argument constructor plus setters makes that process simple for beginners.

STEP 9 · READ

GET All Students

Our first endpoint simply returns the complete ArrayList.

@GetMapping
public List<Student> getAllStudents() {
    return students;
}
GET /api/students
↓
@GetMapping method runs
↓
returns List<Student>
↓
Spring/Jackson converts Java objects to JSON
↓
200 OK

Open http://localhost:8080/api/students in the browser. You should receive a JSON array.

STEP 10 · FIND ONE

GET One Student by ID

The value inside the URL becomes a Java method parameter using @PathVariable.

@GetMapping("/{id}")
public ResponseEntity<Student> getStudentById(@PathVariable int id) {
    for (Student student : students) {
        if (student.getId() == id) {
            return ResponseEntity.ok(student);
        }
    }

    return ResponseEntity.notFound().build();
}
Part Meaning
/{id} Creates a variable section in the URL.
@PathVariable int id Reads that URL value into Java.
ResponseEntity.ok(student) Returns 200 OK with the Student as JSON.
notFound() Returns 404 when no matching student exists.
STEP 11 · CREATE

POST a New Student

The client sends JSON. @RequestBody asks Spring to convert that JSON into a Student object.

@PostMapping
public ResponseEntity<Student> createStudent(@RequestBody Student student) {
    student.setId(nextId++);
    students.add(student);

    return ResponseEntity.status(201).body(student);
}

Example request body

{
  "name": "Neha",
  "age": 22,
  "course": "IT"
}
JSON request
↓ @RequestBody
Student object
↓ set generated ID
↓ students.add(student)
↓ 201 Created + Student JSON

Which HTTP method are we using to create a student?

STEP 12 · UPDATE

PUT to Update a Student

We find the existing student by ID, then copy the new values from the request body.

@PutMapping("/{id}")
public ResponseEntity<Student> updateStudent(
        @PathVariable int id,
        @RequestBody Student updatedStudent) {

    for (Student student : students) {
        if (student.getId() == id) {
            student.setName(updatedStudent.getName());
            student.setAge(updatedStudent.getAge());
            student.setCourse(updatedStudent.getCourse());

            return ResponseEntity.ok(student);
        }
    }

    return ResponseEntity.notFound().build();
}

Example request

PUT /api/students/1

{
  "name": "Asha Sharma",
  "age": 21,
  "course": "AI & ML"
}
Why not replace the ID?

The ID comes from the URL and identifies which existing student we are editing. We only update the editable fields.

STEP 13 · DELETE

DELETE a Student

Use the ID in the URL and remove the matching item from the ArrayList.

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteStudent(@PathVariable int id) {
    for (Student student : students) {
        if (student.getId() == id) {
            students.remove(student);
            return ResponseEntity.noContent().build();
        }
    }

    return ResponseEntity.notFound().build();
}
DELETE /api/students/2
↓ @PathVariable
find matching student
↓ remove from ArrayList
↓
204 No Content

If not found → 404 Not Found
NOTE

We return no response body after a successful delete, so 204 No Content is appropriate.

STEP 14 · PUT IT TOGETHER

Complete StudentController.java

At this point your entire CRUD backend, apart from the generated Spring Boot main class, is in this one file.

package com.example.studentapi;

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

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

@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final List<Student> students = new ArrayList<>();
    private int nextId = 1;

    public StudentController() {
        students.add(new Student(nextId++, "Asha", 20, "CSE"));
        students.add(new Student(nextId++, "Rahul", 21, "ECE"));
    }

    @GetMapping
    public List<Student> getAllStudents() {
        return students;
    }

    @GetMapping("/{id}")
    public ResponseEntity<Student> getStudentById(@PathVariable int id) {
        for (Student student : students) {
            if (student.getId() == id) {
                return ResponseEntity.ok(student);
            }
        }
        return ResponseEntity.notFound().build();
    }

    @PostMapping
    public ResponseEntity<Student> createStudent(@RequestBody Student student) {
        student.setId(nextId++);
        students.add(student);
        return ResponseEntity.status(201).body(student);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Student> updateStudent(
            @PathVariable int id,
            @RequestBody Student updatedStudent) {

        for (Student student : students) {
            if (student.getId() == id) {
                student.setName(updatedStudent.getName());
                student.setAge(updatedStudent.getAge());
                student.setCourse(updatedStudent.getCourse());
                return ResponseEntity.ok(student);
            }
        }
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteStudent(@PathVariable int id) {
        for (Student student : students) {
            if (student.getId() == id) {
                students.remove(student);
                return ResponseEntity.noContent().build();
            }
        }
        return ResponseEntity.notFound().build();
    }

    public static class Student {
        private int id;
        private String name;
        private int age;
        private String course;

        public Student() {
        }

        public Student(int id, String name, int age, String course) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.course = course;
        }

        public int getId() {
            return id;
        }

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

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getCourse() {
            return course;
        }

        public void setCourse(String course) {
            this.course = course;
        }
    }
}
STEP 15 · TEST

Test All CRUD Endpoints

You can use Postman, Thunder Client, REST Client, curl, or your Flutter app.

Operation Method URL
Get all GET /api/students
Get one GET /api/students/1
Create POST /api/students
Update PUT /api/students/1
Delete DELETE /api/students/1
curl http://localhost:8080/api/students
curl -X POST http://localhost:8080/api/students \
  -H "Content-Type: application/json" \
  -d '{"name":"Neha","age":22,"course":"IT"}'
STEP 16 · TRACE IT

Understand One Request End-to-End

Follow what happens when the client creates a student.

1. Client sends POST /api/students + JSON
↓
2. Spring matches @PostMapping
↓
3. Jackson converts JSON → Student object
↓
4. createStudent() runs inside StudentController
↓
5. Controller generates an ID
↓
6. Controller adds Student to ArrayList
↓
7. ResponseEntity creates 201 Created
↓
8. Jackson converts Student → JSON
↓
9. Client receives status + JSON
Remember

The data lives only in RAM. Stop and restart the Spring Boot application and your newly created students disappear.

STEP 17 · REVISION

Student CRUD API Revision

Review the ideas before connecting this backend to Flutter.

What does @RestController do?
It tells Spring that the class contains REST endpoints whose return values should be written to the HTTP response.
What does @RequestMapping("/api/students") do?
It gives every endpoint in the controller the same base URL.
What does @PathVariable do?
It reads a dynamic value such as the student ID from the URL.
What does @RequestBody do?
It tells Spring/Jackson to convert incoming JSON into a Java object.
Where are students stored?
Inside an in-memory ArrayList<Student> declared directly in StudentController.
Why does data disappear after restart?
There is no database. The ArrayList exists only while the Java process is running.

Completion checklist

  • I can create a Spring Boot project from start.spring.io.
  • I can run Spring Boot from VS Code.
  • I understand the generated main class.
  • I can create a REST controller.
  • I can temporarily store objects in an ArrayList.
  • I can create GET, POST, PUT, and DELETE endpoints.
  • I understand @PathVariable.
  • I understand @RequestBody.
  • I can return 200, 201, 204, and 404 responses.
  • I understand that this version has no persistence.

Practice

  1. Add an email field to Student.
  2. Add a third seed student.
  3. Create GET /api/students/course/{course}.
  4. Prevent creating a student when the name is empty.
  5. Connect these endpoints to your Flutter networking app.

First Spring Boot backend complete! 🎉

You now understand CRUD without hiding the logic behind multiple layers. Once this flow is comfortable, you can introduce separate model/service/repository files and a real database.