GymFlow is built as a classic three-tier web application. The Angular single-page application runs in the browser and communicates exclusively through a Symfony REST API. The API validates JWT tokens on every protected request, applies business logic, and persists data in a MariaDB database. All three tiers are packaged as Docker containers and wired together by Docker Compose.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Lokhy87/gymApp/llms.txt
Use this file to discover all available pages before exploring further.
Infrastructure services
Thedocker-compose.yml at the repository root defines two services that run on a shared gymflow_network bridge network.
| Service | Container | Image / Build | Host port | Container port |
|---|---|---|---|---|
| PHP/Symfony API | gymflow_php_server | Built from Dockerfile (PHP 8.3 + Apache) | 8050 | 8000 |
| MariaDB database | gymflow_db_server | mariadb:10.11 | 3350 | 3306 |
gymflow_db_mysql) is a named Docker volume so data persists across container restarts. Both containers are configured with restart: unless-stopped.
The Dockerfile extends php:8.3-apache and adds Composer, the Symfony CLI, the pdo_mysql extension, and Xdebug (port 9003, trigger mode) for local development.
JWT authentication flow
GymFlow uses LexikJWT to issue and validate tokens. The sequence below shows the complete flow from login to an authenticated API call.- Register — the client calls
POST /api/registerwithemail,username, andpassword. TheApiControllerhashes the password with Symfony’sUserPasswordHasherInterfaceand persists the newUserentity. - Login — the client calls
POST /api/login_checkwithusername(email) andpassword. LexikJWT validates the credentials viaUserProvider::loadUserByIdentifier, which looks up the user by email. On success, LexikJWT returns a signed JWT. - Authenticated requests — the client includes the token in every subsequent request as
Authorization: Bearer <token>. Symfony’s security layer decodes and validates the token before the controller action runs. - Frontend interception — the Angular
authInterceptorreads the token fromlocalStorageand clones each outgoingHttpRequestto add theAuthorizationheader automatically. No individual service needs to set headers manually.
The
POST /api/login_check route body uses the key username for the email value. This is a LexikJWT convention — the UserProvider resolves it against the email column in the database.Backend architecture
The Symfony backend follows a standard layered structure underbackend/src/.
Controllers
A single
ApiController handles all public and authenticated REST endpoints under the /api prefix. Routes are defined with PHP 8 attributes (#[Route]). The controller delegates persistence to Doctrine via the EntityManagerInterface and uses injected repositories for reads.Entities
Doctrine ORM entities map directly to database tables. The core entities are
User, Workout, Exercises, MuscleGroups, Muscles, ExercisesMuscles, ExercisesVariants, TrainingGoal, TrainingLevel, TrainingMethod, WorkPlan, and WorkSplit.Repositories
Each entity has a dedicated repository extending Doctrine’s
ServiceEntityRepository. The WorkoutRepository is used directly in the controller for history and progress queries, including a custom QueryBuilder query that filters by user, exercise name, and date range.Security
App\Security\UserProvider implements UserProviderInterface and loads users by email identifier. It also implements PasswordUpgraderInterface to automatically rehash passwords when Symfony detects a weaker algorithm. The stateless JWT firewall means refreshUser is rarely called in practice.Key API endpoints
| Method | Path | Auth required | Description |
|---|---|---|---|
GET | /api | No | Health check — returns project name, version, and status |
POST | /api/register | No | Create a new user account |
POST | /api/login_check | No | Exchange credentials for a JWT |
GET | /api/me | Yes | Return the authenticated user’s profile |
PUT | /api/me | Yes | Update the authenticated user’s profile |
GET | /api/exercises | Yes | List all exercises, optionally filtered by muscle_group_id |
GET | /api/muscle_groups | Yes | List all muscle groups |
GET | /api/exercises_muscles | Yes | List exercise–muscle associations with role data |
POST | /api/workouts | Yes | Log a new workout entry |
PUT | /api/workouts/{id} | Yes | Update an existing workout |
DELETE | /api/workouts/{id} | Yes | Delete a workout (owner-only enforced) |
GET | /api/history | Yes | Return all workouts for the authenticated user |
GET | /api/progress | Yes | Return workout data for a named exercise over N months |
Frontend architecture
The Angular application lives infrontend/src/app/ and is structured into four top-level directories.
Views
Page-level components grouped by feature:
initial-page, login, register (public layout) and home, exercises, history, progress, profile (main layout behind auth). Routing is defined in app.routes.ts using two nested layout components — PublicLayout and MainLayout — that share a common shell.Services
Injectable services wrap every API resource:
AuthService (register, login, profile), WorkoutService (create workout), ExercisesService, MuscleGroupsService, HistoryService, and ProgressService. Each service reads the API base URL from environment.apiUrl (http://localhost:8050/api in development, /api in production).Interceptors
The
authInterceptor functional interceptor reads the JWT from localStorage under the key token, trims whitespace, and clones every outgoing request to add the Authorization: Bearer <token> header. Requests without a stored token are forwarded unchanged.Shared
Reusable building blocks used across views. The
interfaces/ directory defines TypeScript interfaces — Workout, CreateWorkoutRequest, CreateWorkoutResponse, UserProfile, and exercise/muscle-group shapes. The components/ directory contains shared UI components: card, modal, and sidebar.Frontend dependencies
Key packages frompackage.json that shape the application:
| Package | Version | Purpose |
|---|---|---|
@angular/core | ^21.0.0 | Core Angular framework |
@fullcalendar/angular | ^6.1.20 | Calendar view for workout history |
chart.js | ^4.5.1 | Progress charts for strength over time |
bootstrap | ^5.3.8 | UI layout and component styling |
jwt-decode | ^4.0.0 | Decode JWT claims client-side |
rxjs | ~7.8.0 | Reactive streams for HTTP and state |
Environment configuration
The Angular build replacessrc/environments/environment.ts with environment.prod.ts at build time.
apiUrl is a relative path. In development the frontend (ng serve, port 4200) calls the Dockerised API directly on port 8050.
Related pages
Docker deployment
Configure and run GymFlow in production using Docker Compose.
Environment variables
All configurable environment variables for the backend and database.
API authentication
Register, log in, and manage tokens with the authentication endpoints.
Quickstart
Get GymFlow running locally and log your first workout in minutes.