A self-hosted fitness tracking web application built with NestJS and Vue 3.
Grindify is a full-stack workout application designed for users who want full ownership of their data without subscription fees or connectivity requirements. It offers a complete suite of tools to plan routines, log sessions in real-time, and visualize progress over time. The platform is designed to be mobile-first for gym usage while providing a robust desktop interface for planning and analysis.
- Workout Management: Create and organize custom workout routines with specific exercises, sets, and targets.
- Session Tracking: Log workouts in real-time with an interface optimized for mobile devices.
- Exercise Library: Manage a database of exercises with support for custom images and muscle group categorization.
- Progress Analytics: View detailed statistics including volume, frequency, and personal records per exercise.
- Body Metrics: Track weight logs and upload progress photos to monitor physical changes.
- Privacy Focused: Complete data ownership with no third-party tracking or external dependencies.
- Framework: NestJS (v11)
- Database: PostgreSQL 17
- ORM: TypeORM
- Authentication: Passport.js (JWT & Local Strategies)
- Validation: class-validator & class-transformer
- Media: Sharp (Image processing) & Multer (File uploads)
- Documentation: Swagger/OpenAPI
- Framework: Vue 3 (Composition API)
- Build Tool: Vite
- UI Library: Vuetify 3
- State Management: Pinia (with persistence)
- Visualization: Chart.js & Vue-Chartjs
- Routing: Vue Router
- Docker & Docker Compose (Recommended)
- OR
- Node.js v18+
- PostgreSQL v15+
-
Clone the repository
git clone https://github.com/FalkenDev/Grindify.git cd Grindify -
Start the application
docker compose up -d --build
The database migrations will run automatically on startup.
-
Seed initial data (optional) Population of default exercises and muscle groups:
docker exec -it grindify_backend npm run seed -
Access the application
- Frontend: http://localhost:3000
- API Documentation: http://localhost:1337/api
- Backend API: http://localhost:1337
-
Clone the repository
git clone https://github.com/FalkenDev/Grindify.git cd Grindify -
Configure Environment Copy the example environment file and configure your database credentials:
cp .env.example .env
-
Backend Setup
cd backend npm install # Ensure PostgreSQL is running and update .env with credentials npm run build npm run start:prod
-
Frontend Setup
cd frontend npm install npm run build npm run preview
Grindify/
├── backend/ # NestJS API application
│ ├── src/ # Source code
│ └── test/ # E2E tests
├── frontend/ # Vue 3 application
│ └── src/ # Source code
├── docker-compose.yml # Development orchestration
└── Dockerfile.* # Container definitions
To start both services with hot-reload enabled:
docker compose -f docker-compose.yml up- Backend changes will trigger a transparent restart.
- Frontend changes will be reflected instantly via Vite HMR.
Grindify supports optional GitHub sign-in. Leave GITHUB_CLIENT_ID blank to disable it entirely — the app works fine without it.
- Go to github.com/settings/developers → OAuth Apps → New OAuth App
- Fill in:
- Application name: Grindify
- Homepage URL:
http://localhost:3000(or your domain) - Authorization callback URL:
http://localhost:1337/v1/auth/github/callback
- Copy the Client ID and generate a Client Secret
- Add to your
.env:
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
BACKEND_URL=http://localhost:1337 # must match what GitHub redirects to- Restart the backend
If GITHUB_CLIENT_ID is not set, the GitHub button on the login page still appears but the strategy is not loaded — set it to enable the full flow.
Account linking: If a GitHub email matches an existing password-based account, the accounts are automatically linked. The user can then sign in with either method.
Grindify supports optional email-based verification and password reset using Resend.
Set these variables in your .env file:
# Enable email verification (false by default — users can log in immediately after registration)
REQUIRE_EMAIL_VERIFICATION=false
# Resend API key — required only when REQUIRE_EMAIL_VERIFICATION=true
RESEND_API_KEY=re_your_api_key_here
# The "From" address for outgoing emails
EMAIL_FROM=noreply@yourdomain.com
# The public URL of your frontend (used in email links)
FRONTEND_URL=https://yourdomain.com- Create a free account at resend.com.
- Go to API Keys and create a new key.
- Add a verified sending domain under Domains (required to send from your own domain).
- Set
RESEND_API_KEYandEMAIL_FROMin your.env. - Set
REQUIRE_EMAIL_VERIFICATION=trueto enforce verification.
REQUIRE_EMAIL_VERIFICATION |
Behaviour |
|---|---|
false (default) |
Users are auto-verified on registration. No email is sent. Password reset still works if Resend is configured. |
true |
Users receive a 6-digit code by email after registration and must verify before logging in. Login is blocked until verified. |
Password reset (available regardless of the verification toggle):
- User clicks "Forgot password?" on the login page.
- They enter their email — a 6-digit reset code is sent.
- They enter the code + new password on the reset page.
- Codes expire after 15 minutes.
Note: If
REQUIRE_EMAIL_VERIFICATION=falseand no Resend credentials are set, password reset emails will fail silently. Configure Resend if you want password reset to work.
Contributions are welcome. Please read our Contributing Guidelines before submitting a Pull Request.
By contributing to Grindify, you agree to our Contributor License Agreement (CLA).
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See the LICENSE file for details.
This software is provided "as is", without warranty of any kind.