The goal
To provide public proof of my back-end skills in PHP/Symfony: a small but clean, complete and tested REST API that a recruiter can read and run in a few minutes. A task manager is a deliberate choice: a simple scope keeps the focus on code quality rather than business complexity.
The architecture
A REST API over a single Task resource, with a clear separation of concerns:
- a Doctrine entity
Task(title, description, “done” state, priority, creation date); - an input DTO (
TaskInput) kept separate from the entity: incoming data is validated on the DTO, which avoids exposing or polluting the database structure; - a thin controller that orchestrates deserialization, validation, persistence and response;
- persistence in SQLite (zero configuration) with a versioned Doctrine migration.
Technical highlights
- Validation: Symfony constraints on the DTO (required title, length limits, bounded priority). On failure, the API returns a 422 with per-field details.
- Consistent JSON errors:
400(invalid JSON),404(missing resource),422(validation) — always the same shape, never an HTML error page. - Controlled serialization via groups (the Serializer exposes only the intended fields).
- Endpoints: list (with a
?done=filter), show, create, update, toggle state (PATCH …/toggle) and delete.
The tests
The project is covered by PHPUnit, at two levels:
- unit tests on the entity (initial state, done/undone toggle);
- functional tests that actually call the API (create, failed validation, 404, toggle then delete), on a test SQLite database recreated cleanly for each test.
Result: 7 tests, 22 assertions, all green.
Deployment
A Dockerfile packages the application (PHP + extensions + dependencies) for an identical start-up anywhere: the API can be deployed for free as a container (Render, Fly.io, Koyeb), with the schema applied automatically at launch.
What the project demonstrates
- Designing an idiomatic REST API in Symfony (attribute routing, dependency injection, Doctrine).
- Cleanly separating input / domain / persistence (DTO, validation, entity).
- Testing both the logic and the HTTP behaviour.
- Shipping a reproducible project (migrations, Docker) with a readable commit history.
Next steps
JWT authentication to protect the endpoints, pagination and sorting on the list, and OpenAPI / Swagger documentation.