A small API for library staff to track the library's book inventory — add and delete books, list them, and update each book's borrowed / available state (who borrowed it and when).
- PHP 8.3, Symfony 7.4, API Platform 4
- Doctrine ORM + PostgreSQL 16
- JWT auth (Lexik) · Docker Compose · PHPUnit, PHPStan level 8, php-cs-fixer
Needs Docker Compose v2. The app lives in app/.
git clone https://github.com/mar3q/library.git
cd library
make setup # first run: build, install deps, start containers, JWT keys, migrate, seedAfter the first run, just start/stop the stack — make up / make down.
Open Swagger UI: http://localhost:8080/api
curl -X POST http://localhost:8080/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@example.com","password":"admin"}' # -> {"token":"<JWT>"}Send Authorization: Bearer <JWT> on /api/* (or use Authorize in Swagger UI).
| Password | Role | |
|---|---|---|
admin@example.com |
admin |
admin |
employee@example.com |
employee |
user |
All /api/* routes require a valid JWT.
| Method & path | Purpose |
|---|---|
POST /auth/login |
obtain a JWT (public) |
GET /api/books |
list books (filters / sort / pagination) |
POST /api/books |
add a book |
GET /api/books/{id} |
book details |
DELETE /api/books/{id} |
delete a book |
POST /api/books/{id}/borrow |
mark borrowed — { "cardNumber": "123456" } |
POST /api/books/{id}/return |
mark available |
A book has a 6-digit unique serialNumber (entered by staff), title, author, a status
(AVAILABLE / BORROWED), and — while borrowed — the borrower's 6-digit
borrowedByCardNumber and borrowedAt timestamp.
List filters: ?status=AVAILABLE|BORROWED, ?author=, ?title=, ?serialNumber=,
?cardNumber=, ?sort[title|author|createdAt]=asc|desc, ?page=, ?itemsPerPage=.
- Lightweight API Platform State processors (
src/State/Processor/*BookProcessor.php) write through theEntityManagerdirectly; reads use the default Doctrine provider. BookCollectionProviderpowers the list endpoint — combined filters, validated sorting, and pagination.- State workflow lives on the
Bookentity +BookStatusenum. Errors: a malformed (non‑6‑digit) or duplicate serial number, or a malformed card number →422; an invalid state change (borrowing an already‑borrowed book, or returning an available one) →409. - Security — JWT (Lexik); every
/api/*operation requires an authenticated user. - HTTP caching — GET responses carry an
ETag; a conditional GET returns304.
make qa # php-cs-fixer + PHPStan (level 8) + PHPUnitmake test (and make qa) ensures the test database exists and is migrated first. Use
make db-test-reset to drop & recreate it from scratch if the schema drifts.
CI (.github/workflows/ci.yml) runs lint (php-cs-fixer + PHPStan) and PHPUnit on every push
and pull request.