Java backend for Shelf, a tiny personal reading tracker. Owns auth and the User/Book
domain; delegates cover/description lookup to shelf-enrichment-python.
Spring Boot 3.2 (Java 17), Spring Data JPA + H2 (file-based, no server to install), JJWT for auth
tokens, spring-security-crypto for password hashing (no full Security starter — one hand-written
filter is enough for this project).
Controller (AuthController, BookController)
-> Service (AuthService, BookService)
-> Repository (UserRepository, BookRepository : JpaRepository)
-> EnrichmentClient (interface) -> OpenLibraryEnrichmentClient (impl)
-> HTTP call to shelf-enrichment-python
Book/User both extend BaseEntity. BookService.addBook saves the book first, then applies
enrichment as a best-effort step — a failure there (enrichment service down, external API down)
never loses the book itself.
POST /api/auth/login issues a JWT (HS256) signed with SHELF_JWT_SECRET. Every /api/books/**
request must carry it as Authorization: Bearer <token>. shelf-enrichment-python validates the
same token with the same secret — it never talks to this service to check a session.
cp .env.example .env # then `export $(cat .env | xargs)` or set the vars another way
mvn spring-boot:runRuns on http://localhost:8080. The H2 database file is created under ./data/ on first run.
mvn test| Method | Path | Auth | Body | Notes |
|---|---|---|---|---|
| POST | /api/auth/signup |
— | {username, password} |
password min 6 chars |
| POST | /api/auth/login |
— | {username, password} |
returns {token, tokenType} |
| GET | /api/books |
Bearer | — | books owned by the caller |
| POST | /api/books |
Bearer | {title, author} |
saves, then enriches best-effort |