Hey everyone! 👋
So last weekend I challenged myself to build a REST API from scratch as quickly as possible. I've been wanting to level up my Spring Boot skills, and what better way than a good old Todo API, right? Turned out, I got it done in about an hour—not bad!
Here's exactly how I did it, step by step. Whether you're just getting started with Spring Boot or looking for a quick refresher, this should help you get a working API up and running fast.
🚀 Step 1: Initialize the Project
First things first: head over to Spring Initializr (seriously, this tool is a lifesaver).
Here's what I chose:
- Project: Maven
- Language: Java
- Spring Boot: 3.x (latest stable)
-
Dependencies:
- Spring Web
- Spring Data JPA
- H2 Database
Once you've got those selected, hit generate, download the zip, and extract it. Easy!
📁 Step 2: Project Structure
Here's how I organized everything:
src/
└── main/
├── java/com/example/todo/
│ ├── TodoApplication.java
│ ├── controller/TodoController.java
│ ├── model/Todo.java
│ └── repository/TodoRepository.java
│
└── resources/
├── application.properties
└── data.sql (optional seed)
Nothing fancy—just clean separation of concerns.
⚙️ Step 3: Configure application.properties
This is where we tell Spring Boot how to connect to our H2 in-memory database. Super simple setup:
spring.datasource.url=jdbc:h2:mem:todo_db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
The H2 console is awesome for debugging—you can actually see your database in action at http://localhost:8080/h2-console.
🗂️ Step 4: Create the Todo Entity
Time to model our data. Here's the Todo class:
package com.example.todo.model;
import jakarta.persistence.*;
@Entity
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private boolean completed;
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 boolean isCompleted() { return completed; }
public void setCompleted(boolean completed) { this.completed = completed; }
}
Pretty straightforward—just an ID, a title, and a completed flag. JPA handles all the database magic for us.
🔍 Step 5: Create the Repository
This is where Spring Data JPA really shines. You literally just need this:
package com.example.todo.repository;
import com.example.todo.model.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TodoRepository extends JpaRepository<Todo, Long> {
}
Yep, that's it! Spring automatically implements all the CRUD operations for you. No boilerplate code needed.
🎮 Step 6: Build the REST Controller
Now for the fun part—creating our API endpoints:
package com.example.todo.controller;
import com.example.todo.model.Todo;
import com.example.todo.repository.TodoRepository;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/todos")
@CrossOrigin
public class TodoController {
private final TodoRepository repository;
public TodoController(TodoRepository repository) {
this.repository = repository;
}
@GetMapping
public List<Todo> getAll() {
return repository.findAll();
}
@GetMapping("/{id}")
public Todo getById(@PathVariable Long id) {
return repository.findById(id).orElseThrow();
}
@PostMapping
public Todo create(@RequestBody Todo todo) {
return repository.save(todo);
}
@PutMapping("/{id}")
public Todo update(@PathVariable Long id, @RequestBody Todo todo) {
Todo existing = repository.findById(id).orElseThrow();
existing.setTitle(todo.getTitle());
existing.setCompleted(todo.isCompleted());
return repository.save(existing);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
repository.deleteById(id);
}
}
We've got all the RESTful operations covered: GET, POST, PUT, and DELETE. The @CrossOrigin annotation is there in case you want to call this from a frontend later.
🏃 Step 7: Main Application Class
This one's already generated by Spring Initializr, but here it is for completeness:
package com.example.todo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TodoApplication {
public static void main(String[] args) {
SpringApplication.run(TodoApplication.class, args);
}
}
▶️ Step 8: Run It!
Time to see this baby in action. From your project root, run:
mvn spring-boot:run
Your API should now be live at http://localhost:8080/api/todos!
Want to peek at the database? Hit up http://localhost:8080/h2-console and use the JDBC URL jdbc:h2:mem:todo_db.
🧪 Step 9: Test the API
Here's where you can use curl or Postman to test everything out:
Create a new todo:
curl -X POST -H "Content-Type: application/json" \
-d '{"title":"Learn Spring Boot","completed":false}' \
http://localhost:8080/api/todos
Get all todos:
curl http://localhost:8080/api/todos
Update a todo:
curl -X PUT -H "Content-Type: application/json" \
-d '{"title":"Learn Spring Boot","completed":true}' \
http://localhost:8080/api/todos/1
Delete a todo:
curl -X DELETE http://localhost:8080/api/todos/1
And that's it! You've got a fully functional REST API.
🎯 What I Learned
- Spring Boot is ridiculously fast for prototyping—most of the boilerplate is handled for you
- H2 is perfect for development—no need to set up a full database just to test things out
- JPA repositories are magical—seriously, that one-line interface saves so much time
🔮 What's Next?
This is just the foundation. From here, you could:
- Add validation with
@Validand Bean Validation - Implement exception handling with
@ControllerAdvice - Add security with Spring Security
- Switch to a production database like PostgreSQL or MySQL
- Write unit and integration tests
💭 Final Thoughts
Building this took me about an hour, and honestly, most of that was me making sure I had clean, readable code. If you're just learning Spring Boot, don't get overwhelmed by all the "enterprise" talk—start with something simple like this, get it working, and build from there.
What do you think? Have you built something similar? Any tips or improvements you'd suggest? Drop a comment below—I'd love to hear your thoughts! 🚀
Happy coding! ✨
Top comments (0)