Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

GymHero

Simple application to manage your gym training workouts. You have the flexibility to create your own exercises, you can develop custom training units and these units can be easily integrated into personalized training plans. You can manage your training units by adding or removing exercises as needed, each with an optional prescription — a list of sets, every set with its own reps and weight. By default application contains database of more than 1000 exercises.

Frontend

A modern SPA (Vite + React + TypeScript, Tailwind + shadcn/ui, TanStack Router/Query) lives in frontend/. It is fully type-safe against this API's OpenAPI schema. See frontend/README.md for setup — in short: mise run dev (backend on :8000), then cd frontend && npm install && npm run gen:api && npm run dev.

Motivation

To build an CRUD API with FastAPI, SQLAlchemy, Postgres, Docker

Data Models

  • Exercise
  • ExerciseType
  • Level
  • BodyPart
  • TrainingUnit
  • TrainingPlan
  • User

Entity Relationship Diagram

erDiagram
    users          ||--o{ exercises                   : creates
    users          ||--o{ training_units              : creates
    users          ||--o{ training_plans              : creates

    body_parts     ||--o{ exercises                   : targets
    exercise_types ||--o{ exercises                   : types
    levels         ||--o{ exercises                   : rates

    training_units ||--o{ training_unit_exercise      : has
    exercises      ||--o{ training_unit_exercise      : in
    training_unit_exercise ||--o{ prescribed_set      : prescribes

    training_plans ||--o{ training_plan_training_unit : has
    training_units ||--o{ training_plan_training_unit : in

    users {
        int      id              PK
        string   email           UK
        string   full_name
        string   hashed_password
        bool     is_active
        bool     is_superuser
        int      token_version
        datetime created_at
        datetime updated_at
    }
    exercises {
        int      id                  PK
        string   name                UK
        string   description
        int      target_body_part_id FK
        int      exercise_type_id    FK
        int      level_id            FK
        int      owner_id            FK
        datetime created_at
        datetime updated_at
    }
    exercise_types {
        int      id         PK
        string   name       UK
        datetime created_at
        datetime updated_at
    }
    levels {
        int      id         PK
        string   name       UK
        datetime created_at
        datetime updated_at
    }
    body_parts {
        int      id         PK
        string   name       UK
        datetime created_at
        datetime updated_at
    }
    training_units {
        int      id          PK
        string   name        "unique per owner"
        string   description
        int      owner_id    FK
        datetime created_at
        datetime updated_at
    }
    training_plans {
        int      id          PK
        string   name        "unique per owner"
        string   description
        int      owner_id    FK
        datetime created_at
        datetime updated_at
    }
    training_unit_exercise {
        int id               PK
        int training_unit_id FK "unique per (unit, exercise)"
        int exercise_id      FK
    }
    prescribed_set {
        int   id                        PK
        int   training_unit_exercise_id FK
        int   set_number
        int   reps                      "nullable"
        float weight                    "nullable"
    }
    training_plan_training_unit {
        int training_plan_id PK, FK
        int training_unit_id PK, FK
    }

Core technologies

  • FastAPI - web framework for building APIs with Python 3.8+ based on standard Python type hints.
  • SQLAlchemy - Object Relational Mapper
  • Pydantic - Data validation library for Python and FastAPI models
  • Uvicorn - ASGI web server implementation for Python
  • Alembic - lightweight database migration tool for usage with the SQLAlchemy Database Toolkit for Python.
  • Docker - tool to package and run an application in a loosely isolated environment
  • Docker Compose - tool for defining and running multi-container Docker applications
  • Postgres - open source object-relational database
  • For testing:
    • pytest
    • pytest-cov
    • pytest-mock
  • For development
    • mise - toolchain manager (pins Python + uv)
    • uv - dependency management and virtualenvs
    • ruff - linting and formatting
    • mypy - static type checking
    • pre-commit

Implemented functionalities

  • JWT Authentication
  • Password Hashing
  • Login & Register Endpoints
  • ORM Objects representing SQL tables and relationships
  • Pydantic schemas
  • CRUD module for reading, updating, deleting objects in/from database
  • Pagination
  • Dependencies - superuser, active user, database
  • Initialization scripts
  • Separate database and env for testing

Define use cases:

All API routes are served under the /api/v1 prefix — e.g. GET /api/v1/exercises/all. (/health, /ready and the Swagger docs at /docs are not prefixed.)

Exercises

RoutesMethodEndpointAccess
/exercisesGET/allActive User
/exercisesGET/myOwner
/exercisesGET/{exercise_id}Active User
/exercisesDELETE/{exercise_id}Superuser, Owner
/exercisesPATCH/{exercise_id}Superuser, Owner
/exercisesGET/name/{exercise_name}Active User
/exercisesPOSTActive User

ExerciseType

RoutesMethodEndpointAccess
/exercise-typesGET/allAll
/exercise-typesGET/{exercise_type_id}All
/exercise-typesDELETE/{exercise_type_id}Superuser
/exercise-typesPUT/{exercise_type_id}Superuser
/exercise-typesGET/name/{exercise_type_name}All
/exercise-typesPOSTSuperuser

Levels

RoutesMethodEndpointAccess
/levelsGET/allAll
/levelsGET/{level_id}All
/levelsDELETE/{level_id}Superuser
/levelsPUT/{level_id}Superuser
/levelsGET/name/{level_name}All
/levelsPOSTSuperuser

Body Parts

RoutesMethodEndpointAccess
/body-partsGET/allAll
/body-partsGET/{bodypart_id}All
/body-partsDELETE/{bodypart_id}Superuser
/body-partsPUT/{bodypart_id}Superuser
/body-partsGET/name/{bodypart_name}All
/body-partsPOSTSuperuser

Users

RoutesMethodEndpointAccess
/usersGET/allSuperuser
/usersGET/{user_id}Superuser
/usersDELETE/{user_id}Superuser
/usersPUT/{user_id}Superuser
/usersGET/email/{email}Superuser
/usersPOSTSuperuser

Auth

RoutesMethodEndpointAccess
/authPOST/loginAll
/authPOST/registerAll
/authPOST/refreshAll
/authPOST/logoutActive User

Training Plans

RoutesMethodEndpointAccess
/training-plansGET/allSuperuser
/training-plansGET/all/myOwner, Superuser
/training-plansGET/{training_plan_id}Owner, Superuser
/training-plansGET/name/{training_plan_name}Owner, Superuser
/training-plansGET/{training_plan_id}/training-unitsOwner, Superuser
/training-plansDELETE/{training_plan_id}Owner, Superuser
/training-plansPUT/{training_plan_id}Owner, Superuser
/training-plansPOSTOwner, Superuser
/training-plansPUT/{training_plan_id}/training-units/{training_unit_id}Owner, Superuser
/training-plansDELETE/{training_plan_id}/training-units/{training_unit_id}Owner, Superuser

Training Units

RoutesMethodEndpointAccess
/training-unitsGET/allSuperuser
/training-unitsGET/all/myOwner, Superuser
/training-unitsGET/{training_unit_id}Owner, Superuser
/training-unitsGET/name/{training_unit_name}Owner, Superuser
/training-unitsGET/{training_unit_id}/exercisesOwner, Superuser
/training-unitsDELETE/{training_unit_id}Owner, Superuser
/training-unitsPUT/{training_unit_id}Owner, Superuser
/training-unitsPOSTOwner, Superuser
/training-unitsPUT/{training_unit_id}/exercises/{exercise_id}Owner, Superuser
/training-unitsPATCH/{training_unit_id}/exercises/{exercise_id}Owner, Superuser
/training-unitsDELETE/{training_unit_id}/exercises/{exercise_id}Owner, Superuser

Private superuser endpoints

RoutesMethodEndpointAccess
/training-unitsGET/name/{training_unit_name}/superuserSuperuser
/training-plansGET/name/{training_plan_name}/superuserSuperuser

How to run

You should have

  • Running Docker
  • mise — manages Python + uv and runs the project tasks (mise run <task>)

clone repository:

git clone https://github.com/JakubPluta/gymhero.git

and navigate to cloned project

build and run project:

# build image, start containers (detached) and run migrations + db seed
mise run dev

alternatively you can use docker commands directly:


docker compose build
docker compose up -d 
docker compose exec app alembic upgrade head
docker compose exec app python -m scripts.seed --env=dev

or

docker compose build --no-cache
docker compose up -d --force-recreate
docker compose exec app alembic upgrade head
docker compose exec app python -m scripts.seed --env=dev

next time you can just start the stack:

mise run up
# or directly
docker compose up -d

to (re)initialize the db:

mise run migrate && mise run seed

to stop the stack:

mise run down

to run tests (spins a Postgres testcontainer — needs Docker):

mise run test          # full suite
mise run test-cov      # with coverage

to lint / type-check:

mise run lint          # ruff format --check + ruff check + mypy
mise run lint-fix      # auto-format and fix

alembic commands (run inside the app container):

mise run migrate         # upgrade to head
mise run makemigration   # autogenerate a revision
# or directly:
docker compose exec app alembic upgrade head
docker compose exec app alembic downgrade -1

Configuration

All settings come from .env.defaults (committed dummy values). Override any of them with a git-ignored .env or real environment variables. Tests set ENV=test and get their database from a Postgres testcontainer.

mise run dev seeds the database and creates the first superuser from those defaults:

FIRST_SUPERUSER_USERNAME=admin
FIRST_SUPERUSER_EMAIL=admin@example.com
FIRST_SUPERUSER_PASSWORD=changeme

Change them via a local .env before running against anything real.

So as you first user is created and app is running you need to generate JWT Token to access different endpoints. To do that use:

curl -X 'POST' \
  'http://localhost:8000/api/v1/auth/login' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=&username=admin%40example.com&password=changeme&scope=&client_id=&client_secret='

In response you will receive something like this:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...",
  "token_type": "bearer"
}

And you need to use it in headers when calling other endpoints eg:

curl -X 'GET' \
  'http://localhost:8000/api/v1/exercises/my?skip=0&limit=10' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDMzMzY5MDYsInN1YiI6IjEifQ.mnbKswazYV8pBv5JWlHv-qJ8fHZ4msW6yWwvRWzKUz4'

To register new user (it will be normal user not superuser, so some routes won't be available)

curl -X 'POST' \
  'http://localhost:8000/api/v1/auth/register' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "mynewuser@mail.com",
  "password": "mypassword",
  "full_name": "My User"
}'

You can also do everything by using fast api docs which are more user friendly and more convenient way to play with api. To do that check http://localhost:8000/docs (your app needs to run)

Possible future work

  • Default training plans (FBW / PPL / Split) seeded for every user
  • Redis cache for the exercise catalog

关于 About

Simple CRUD application built on top of FastAPI, SQLAlchemy, Alembic and Postgres
crudfastapipython3sqlalchemy

语言 Languages

Python53.0%
TypeScript45.7%
CSS0.9%
Dockerfile0.3%
Mako0.1%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
3
Total Commits
峰值: 3次/周
Less
More

核心贡献者 Contributors