The backend that powers the MaxMethod fitness app. It generates personalized strength/hypertrophy/weight-loss programs, tracks workout logs, and exposes the REST API consumed by the React client.
The system is split into two layers:
- Node.js API (
server.js+src/) — Express/MongoDB service that owns user data, programs, and workout logs. - Python AI microservices (
AI_tools/) — two Flask apps the Node API calls during program generation: an exercise selector and a working-weight predictor.
| Layer | Tech |
|---|---|
| API | Node.js (ES modules), Express 5 |
| Database | MongoDB (mongodb driver, Maxmethod_db) |
| Auth | bcrypt + email/password (no JWT yet) |
| Nodemailer | |
| AI services | Python 3, Flask, gunicorn |
| Exercise model | scikit-learn |
| Weight model | XGBoost |
Backend_structure/
├── server.js # Express entry point
├── package.json
├── prisma/ # legacy schema (Mongo is the live store)
├── src/
│ ├── config/database.js # Mongo client + DB handle
│ ├── routes/userRoutes.js # all /api/users/* endpoints
│ ├── controllers/ # classification, auth, etc.
│ ├── middleware/ # auth, validate, rateLimiter, errorHandler
│ ├── models/users.js
│ └── utils/
│ ├── exerciseSelector.js # HTTP client → exercise_selector_tool
│ ├── weightPredictor.js # HTTP client → weight_picker_tool
│ ├── cardioSelector.js # rules-based cardio picker (weight-loss)
│ ├── cardioAssignmentHistory.js
│ ├── cardioMachineMetadata.js
│ ├── userWeightHistory.js # per-user weight correction map
│ ├── sendEmail.js
│ └── ...
└── AI_tools/
├── exercise_selector_tool/ # Flask app on :5001
└── weight_picker_tool/ # Flask app on :5002
POST /api/users/goals is the heart of the backend. Given a user's
classification, days/week, and goal, it:
- Looks up a matching template in
workout_templates(tagged by classification, focus, days/week). - For every non-fixed slot in every week, calls the Python exercise selector to pick a movement based on the user's history and mesocycle.
- For non-bodyweight slots, calls the Python weight predictor to project
working weights from the user's 1RMs (squat/bench/deadlift), then applies a
per-user correction factor built from logged history
(
userWeightHistory.js). - For weight-loss templates, replaces cardio AI calls with the rules-based
cardioSelectorto balance machine variety across the week. - Persists the resolved program to
workout_logs, registers it inprogram_logs, and links it to the user ascurrent_workout_id.
If either Python service is unreachable, the Node helpers fall back gracefully (random pick / null weight) so generation never hard-fails.
| Collection | Purpose |
|---|---|
users |
Profiles, 1RMs, personal bests, custom exercises |
workout_templates |
Source-of-truth program templates (tagged for lookup) |
workout_logs |
Resolved programs (weeks → days → slots) |
program_logs |
One row per program a user has generated/customized |
quick_sessions |
Ad-hoc no-program workouts |
library_videos |
exercise_name → mux_playback_id for video lookup |
Create .env in Backend_structure/:
MONGODB_URI=mongodb+srv://...
PORT=5050
EXERCISE_SELECTOR_URL=http://localhost:5001
WEIGHT_PREDICTOR_URL=http://localhost:5002
# email config used by sendEmail.js, e.g. SMTP_HOST / SMTP_USER / SMTP_PASS
npm install
npm run dev # nodemon
# or
npm start # node serverThe server listens on PORT (default 5050) and mounts routes at
/api/users/*. CORS is open to the local Vite dev server
(http://localhost:5173) and the production domains.
Each tool is a standalone Flask app with its own requirements.txt.
# Exercise selector — port 5001
cd AI_tools/exercise_selector_tool
pip install -r requirements.txt
python service.py
# Weight predictor — port 5002
cd AI_tools/weight_picker_tool
pip install -r requirements.txt
python service.pyOverride the port with PORT=.... Both services include a Procfile for
gunicorn-based deploys.
All routes are mounted under /api/users. Selected endpoints:
POST /create-accountPOST /loginGET /profile/:userIdPUT /update/:userIdPUT /change-password/:userId
POST /classification— compute strength classification from 1RMs/bodyweightPOST /goals— generate a full program (see flow above)
GET /workout/:userId— current active workoutGET /workout-log/:workoutLogIdPATCH /workout/log— log set weight/reps/notes/cardioPATCH /workout/complete-dayPOST /workout/pb-checkGET /workout/:userId/personal-bestsGET /workout/:userId/exercise-history?exercise=...GET /workout/:userId/all-history
POST /custom-workoutPOST /quick-sessionsGET /program-logs/:userIdPATCH /program-logs/set-activePATCH /program-logs/deselectDELETE /program-logs/:programLogIdPATCH /workout-log/:workoutLogId/weeksPATCH /workout-log/:workoutLogId/swap-exercise-all-weeksPATCH /workout-log/:workoutLogId/slot-exercisePATCH /workout-log/:workoutLogId/title
GET /:userId/custom-exercisesPOST /:userId/custom-exercisesDELETE /:userId/custom-exercises/:name
GET /library-videosGET /debug-templates— dev helper, lists all template tags