A RESTful backend for managing folder-based notes with reusable tags, built with Spring Boot and Spring Data JPA.
This backend provides a cleanly designed REST API for folder-based notes with reusable tags, featuring:
- Full CRUD (Create, Read, Update, Delete)
- Partial updates (
PATCH) and full replacement (PUT) - Clear, conventional HTTP status codes
- Centralized error handling
- Auto-managed
createdAttimestamps - In-memory H2 database for easy development
- Java 17+
- Spring Boot
- Spring Web
- Spring Data JPA (declarative transactions with
@Transactional) - H2 Database (dev)
- Jackson (JSON)
Design choices:
- REST semantics (
PUTvsPATCH) - Service-layer invariants (validation + business rules)
src/main/java/com/practice/notes/
NotesApplication.java
├── controller/
│ └── NoteController.java
│ └── FolderController.java
├── exceptions/
│ ├── BadRequestException.java
│ ├── NotFoundException.java
│ └── GlobalExceptionHandler.java
├── model/
│ └── Note.java
│ └── Folder.java
│ └── Tag.java
├── repository/
│ └── NoteRepository.java
│ └── FolderRepository.java
│ └── TagRepository.java
└── service/
└── NoteService.java
└── FolderService.java
src/main/resources/
application.properties
Note
| Field | Description |
|---|---|
| id | Auto-generated primary key |
| title | Required |
| content | Required |
| folder | Each note belongs to a folder |
| tags | Many-to-many tags (stored separately) |
| createdAt | Automatically set on creation |
| pinned | Pin state |
Folder
| Field | Description |
|---|---|
| id | Auto-generated primary key |
| name | Required |
Tag
| Field | Description |
|---|---|
| id | Auto-generated primary key |
| name | Unique tag name |
Tag behavior
- Tags are created implicitly when notes are created or updated.
- Tags do not have an independent lifecycle or controller.
- Tag resolution is centralized in
NoteService.
| Method | Endpoint | Description |
|---|---|---|
| POST | /folders/{folderId}/notes |
Create a note in a folder |
| GET | /notes/{id} |
Get note by ID |
| GET | /folders/{folderId}/notes |
Get notes by folder |
| PATCH | /notes/{id} |
Partial update |
| PUT | /notes/{id} |
Full replace |
| PATCH | /notes/{id}/pin |
Pin a note |
| DELETE | /notes/{id} |
Delete a note |
Notes:
- Folder ID comes from the URL path, not the request body.
- Controllers stay thin; service layer enforces invariants.
| Method | Endpoint | Description |
|---|---|---|
| POST | /folders |
Create a folder |
| GET | /folders |
Get all folders |
| DELETE | /folders/{id} |
Delete a folder |
The API returns errors in a consistent format using custom exceptions and a global handler. Errors are thrown from the service layer so controllers remain lightweight.
Example: 404 — Not Found
{
"error": "NOT_FOUND",
"message": "Note not found"
}Example: 400 — Bad Request
{
"error": "BAD_REQUEST",
"message": "Title is required"
}- Uses H2 in-memory database for development
- Schema auto-generated via JPA
- Data resets on app restart
-
Clone the repository
git clone https://github.com/YOUR_USERNAME/SpringBoot-Notes-Api.git -
Run the application
mvn spring-boot:run -
Access the API:
http://localhost:8080
- Models real relationships (folders, notes, tags) instead of flat CRUD.
- Enforces rules in services, not controllers.
- Avoids unnecessary controllers (tags are managed implicitly).
- Keeps API intent clear with REST semantics.