Skip to content

gbelvedere/backend-exercise

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

backend-exercise

Backend exercise built with Symfony 7.4. Focus areas:

  • Domain-first design (Book, BookId, Person) with validation and immutability.
  • Application layer with contracts and use-case handlers (search, get by id).
  • Infrastructure adapters for Gutendex (HTTP client + mapper) and HTTP controller/transformer.

Prerequisites

  • PHP 8.2+
  • Composer
  • MySQL (if you need DB locally; current flow uses Gutendex API)
  • Symfony CLI optional (for local server tooling)

Setup (without Docker)

cd server
composer install
cp .env .env.local   # adjust APP_SECRET and GUTENDEX_BASE_URL if needed

Run the app (without Docker)

cd server
symfony serve -d   # or php -S localhost:8000 -t public

Run with Docker (Symfony + MySQL + Nginx)

cd docker/excercise
export USER_ID=$(id -u) USER_NAME=$(id -un)
export DB_DATABASE=exercise DB_USERNAME=exercise DB_PASSWORD=exercise
export NGINX_ENV=local
docker compose -f docker-compose.yml up -d --build

Services/ports:

Redis service is optional; keep it running only if you want response caching for Gutendex calls.

Code is mounted from server/ into the container; edits on host are reflected live.

API endpoints

  • GET /books?search=<query>: searches books via Gutendex, returns an array of books.
  • GET /books/{id}: returns a single book by Gutenberg id.

API Documentation

  • Swagger UI: https://exercise.local/api/doc
  • OpenAPI JSON: https://exercise.local/api/doc.json
  • Example calls (self-signed, use -k):
  • Base URL is configurable via API_BASE_URL (default: https://exercise.local).
    • curl -k "https://exercise.local/books?search=Frankenstein"
    • curl -k "https://exercise.local/books/1342"

Response shape (BookJsonTransformer)

{
	"id": 1342,
	"title": "Pride and Prejudice",
	"subjects": ["Love", "Society"],
	"authors": [
		{"name": "Jane Austen", "birth_year": 1775, "death_year": 1817}
	]
}

Error codes

  • 400 { "error": "invalid_id" }
  • 404 { "error": "book_not_found" }
  • 503 { "error": "upstream_unavailable" } (Gutendex/network issue)
  • 500 { "error": "internal_error" }

How to test endpoints locally

  1. Start the server (see Run the app).
  2. Search: curl "http://127.0.0.1:8086/books?search=Frankenstein" (use 8000 if running symfony serve).
  3. Detail: curl "http://127.0.0.1:8086/books/1342"
  4. Not found: curl "http://127.0.0.1:8086/books/999999" → 404.

Running tests

cd server
php bin/phpunit

Utilities

  • Cache check: php bin/console app:redis:check writes/reads a test item in the Redis-backed book catalog cache and lists sample keys.

Environment

Redis caching (optional)

Purpose

  • Cache Gutendex responses to speed up repeated searches and book lookups. Domain/Application remain untouched; a decorator wraps the Gutendex adapter.

Enable/disable

  • Toggle: BOOK_CATALOG_CACHE_ENABLED (1 on, 0 off). When off, calls bypass cache.

Configuration

  • REDIS_DSN (default: redis://redis:6379) — only required when cache is enabled.
  • BOOK_CATALOG_CACHE_TTL (default: 300 seconds); negative lookups cache for 60 seconds.

Keys

  • Namespaced keys ending with books.search.{sha1(normalized query)} and books.id.{id} (namespace added by Symfony cache).

How to verify

  1. Make the same request twice and observe X-BookCatalog-Cache header (MISS then HIT).
  2. Run the diagnostic command (Redis running): php bin/console app:redis:check to write/read a probe item and list sample keys.

API Documentation (OpenAPI / Swagger)

This project exposes an OpenAPI specification generated by NelmioApiDocBundle.

Endpoints

  • Swagger UI (interactive docs): GET /api/doc
  • OpenAPI JSON (raw spec): GET /api/doc.json

Usage

  • Open https://exercise.local/api/doc in your browser to explore and try the endpoints.
  • Use https://exercise.local/api/doc.json to download/consume the OpenAPI spec (e.g., Postman, Swagger tools, CI validation).

Notes

  • The JSON spec is generated dynamically from the application routes and API configuration.
  • If you change routes or API annotations/attributes, clear the Symfony cache to refresh the output.

About

aplicación de búsqueda y listado

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors