A comprehensive fitness program management application built with TypeScript, React, Node.js, and PostgreSQL. Manage workout programs, cycles, blocks, sessions, exercises, and sets with a hierarchical structure designed for coaches and athletes.
- User Management: Secure authentication, profile management, password hashing with bcrypt
- Program Hierarchy: Users > Programs > Cycles > Blocks > Sessions > Exercises > Sets
- CRUD Operations: Full Create, Read, Update, Delete (soft delete) support for all resources
- Pagination: Efficient data retrieval with configurable page sizes
- HATEOAS: Discoverable REST API with hypermedia links
- Modern Stack: React 19, TypeScript, Vite
- Routing: React Router v7 with protected routes
- Authentication: Context-based auth with session management
- Component Library: Reusable UI components (Button, Input, Card)
- CSS Modules: Scoped styling for maintainability
- API Integration: Type-safe API client with error handling
- ✅ Password Hashing: Bcrypt with configurable rounds
- ✅ Session Management: Database-backed sessions (TypeORM store, scalable to Redis)
- ✅ Authentication: Secure session-based auth on all protected endpoints
- ✅ Security Headers: Helmet.js for XSS, clickjacking protection
- ✅ Rate Limiting: Configurable request limits per IP
- ✅ CORS: Whitelist-based cross-origin resource sharing
- ✅ Input Validation: class-validator decorators on all DTOs
- ✅ SQL Injection Protection: TypeORM parameterized queries
- ✅ Soft Deletes: Data retention with soft delete support
- TypeScript: Full type safety throughout
- OpenAPI/Swagger: Interactive API documentation at
/docs - Structured Logging: Winston with file and console transports
- Error Handling: Standardized error responses, production-safe messages
- Environment Variables: Secure configuration with
.envsupport - Database Migrations: Ready for production (currently using sync for dev)
- Comprehensive Tests: Integration test suite with Jest
- Monorepo Structure: PNPM workspaces for shared code
- Prerequisites
- Installation
- Configuration
- Running the Application
- API Documentation
- Authentication
- API Endpoints
- Database Schema
- Testing
- Deployment
- Project Structure
- Contributing
- Node.js: >= 18.x
- PNPM: >= 8.x
- PostgreSQL: >= 14.x
- Docker (optional): For containerized deployment
-
Clone the repository
git clone <repository-url> cd PwrProgram
-
Install dependencies
pnpm install
-
Set up environment variables
cp .env.example apps/pwrprogram/.env
Edit
apps/pwrprogram/.envwith your configuration (see Configuration) -
Set up PostgreSQL database
# Create database createdb pwrprogram # Or use Docker docker run --name pwrprogram-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:14
-
Start the development server
pnpm --filter @pwrprogram/api dev
The API will be available at
http://localhost:3000
All configuration is done through environment variables. Copy .env.example to .env and configure:
NODE_ENV: Environment (development/production)PORT: Server port (default: 3000)API_BASE_URL: Base URL for the API
DB_HOST: PostgreSQL hostDB_PORT: PostgreSQL port (default: 5432)DB_USERNAME: Database usernameDB_PASSWORD: Database passwordDB_DATABASE: Database name
SESSION_SECRET: REQUIRED - Secret key for session signing (min 32 characters)SESSION_NAME: Cookie name (default: pwrprogram.sid)SESSION_MAX_AGE: Session duration in ms (default: 604800000 = 7 days)BCRYPT_ROUNDS: Password hashing rounds (default: 10)
RATE_LIMIT_WINDOW_MS: Time window in ms (default: 900000 = 15 min)RATE_LIMIT_MAX_REQUESTS: Max requests per window (default: 100)
CORS_ORIGIN: Allowed origins, comma-separated (default: http://localhost:3000,http://localhost:5173)
LOG_LEVEL: Logging level (default: info)LOG_FILE_PATH: Log file location (default: logs/app.log)
SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASSWORD,SMTP_FROM: Email configurationFRONTEND_URL: Frontend URL for email links
DB_HOSTDB_USERNAMEDB_PASSWORDDB_DATABASESESSION_SECRET
# Start API server with hot reload
pnpm dev:api
# Start frontend dev server
pnpm dev:web
# Run API tests
pnpm test:api
# Run tests with coverage
pnpm test:api:coverage
# Lint frontend code
pnpm lint:web# Build the API
pnpm --filter @pwrprogram/api build
# Build the frontend
pnpm build:web
# Start production API server
pnpm start:api# Build image
docker build -t pwrprogram .
# Run container
docker run -p 3000:3000 --env-file .env pwrprogramVisit http://localhost:3000/docs for interactive Swagger UI documentation.
http://localhost:3000/api
POST /api/auth/register
Content-Type: application/json
{
"email": "[email protected]",
"password": "securepassword123",
"firstName": "John",
"lastName": "Doe"
}POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "securepassword123"
}GET /api/auth/mePOST /api/auth/logoutPATCH /api/users/:id
Content-Type: application/json
{
"firstName": "Jane",
"lastName": "Smith",
"email": "[email protected]"
}PATCH /api/users/:id
Content-Type: application/json
{
"currentPassword": "oldpassword123",
"newPassword": "newpassword456"
}- Register or login to receive a session cookie
- Browser automatically sends cookie with subsequent requests
- All
/api/*endpoints (except/api/auth/*) require authentication - Session expires after 7 days (configurable)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
Login with credentials |
| POST | /api/auth/logout |
Logout current user |
| GET | /api/auth/me |
Get current user info |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users |
List all users (paginated) |
| GET | /api/users/:id |
Get user by ID |
| POST | /api/users |
Create user (admin) |
| PATCH | /api/users/:id |
Update own profile |
| DELETE | /api/users/:id |
Delete own account (soft delete) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/programs |
List user's programs |
| GET | /api/programs/:id |
Get program by ID |
| POST | /api/programs |
Create new program |
| PATCH | /api/programs/:id |
Update program |
| DELETE | /api/programs/:id |
Delete program (soft delete) |
Similar CRUD operations available for all resources. See Swagger docs for details.
All list endpoints support pagination:
GET /api/users?page=1&limit=20Response format:
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"totalPages": 8
}
}User (1) ─── (N) Program
│
└─ (1) ─── (N) Cycle
│
└─ (1) ─── (N) Block
│
└─ (1) ─── (N) Session
│
└─ (1) ─── (N) Exercise
│
└─ (1) ─── (N) Set
- Soft Deletes: All entities have
deletedAttimestamp - Timestamps:
createdAtandupdatedAton all entities - Cascade Deletes: Deleting parent cascades to children
- Indexes: Foreign keys and email fields indexed
- UUID Primary Keys: For better distribution and security
# Run all tests
pnpm --filter @pwrprogram/api test
# Run with coverage
pnpm --filter @pwrprogram/api test:coverage
# Run specific test file
pnpm --filter @pwrprogram/api test user.testsrc/testing/
├── tests/
│ └── routes/ # Integration tests for each route
├── utils/
│ ├── test-data-source.ts # Test database configuration
│ └── test-helper.ts # Test utilities
└── setup.ts # Jest setup
import { testHelper } from '../utils/test-helper';
describe('Users API', () => {
beforeAll(async () => {
await testHelper.initialize();
});
afterAll(async () => {
await testHelper.cleanup();
});
it('should create a user', async () => {
const response = await testHelper.request
.post('/api/users')
.send({ email: '[email protected]', ... });
expect(response.status).toBe(201);
});
});-
Environment Variables
- Set strong
SESSION_SECRET(min 32 chars) - Configure production database
- Set
NODE_ENV=production - Configure CORS for your domain
- Set up email SMTP (when ready)
- Set strong
-
Database
- Run database migrations (when implemented)
- Set up automated backups
- Configure connection pooling
-
Security
- Use HTTPS (set
secure: trueon cookies) - Configure firewall rules
- Set up monitoring and alerts
- Review rate limiting settings
- Use HTTPS (set
-
Logging
- Configure log aggregation service
- Set appropriate log levels
- Set up error tracking (e.g., Sentry)
# Build
docker build -t pwrprogram:latest .
# Run
docker run -d \
-p 3000:3000 \
--env-file .env.production \
--name pwrprogram \
pwrprogram:latest-
Session Store: Migrate to Redis for horizontal scaling
// In production, replace TypeormStore with RedisStore import RedisStore from 'connect-redis'; import { createClient } from 'redis'; const redisClient = createClient(); store: new RedisStore({ client: redisClient });
-
Database: Use read replicas for read-heavy workloads
-
Load Balancer: Use nginx or cloud load balancer
-
CDN: Serve static assets from CDN
PwrProgram/
├── apps/
│ ├── pwrprogram/ # Main API application
│ │ ├── src/
│ │ │ ├── entity/ # TypeORM entities
│ │ │ ├── routes/ # Express route handlers
│ │ │ ├── middleware/ # Custom middleware
│ │ │ ├── mappers/ # Entity to DTO mappers
│ │ │ ├── utils/ # Utility functions
│ │ │ ├── openapi/ # OpenAPI spec
│ │ │ ├── testing/ # Test files
│ │ │ ├── data-source.ts # TypeORM configuration
│ │ │ └── index.ts # Application entry point
│ │ ├── .env # Environment variables (gitignored)
│ │ └── package.json
│ └── web/ # React frontend application
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── auth/ # Auth-related components
│ │ │ ├── layout/ # Layout components
│ │ │ ├── programs/ # Program management
│ │ │ └── ui/ # Reusable UI components
│ │ ├── context/ # React contexts (Auth)
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # Utilities and API client
│ │ ├── pages/ # Page components
│ │ ├── types/ # TypeScript types
│ │ ├── App.tsx # App with routing
│ │ └── main.tsx # Entry point
│ ├── vite.config.ts # Vite configuration
│ └── package.json
├── packages/
│ └── shared/ # Shared DTOs and types
│ ├── src/
│ │ ├── *.dto.ts # Data Transfer Objects
│ │ └── index.ts
│ └── package.json
├── .env.example # Environment template
├── .gitignore
├── pnpm-workspace.yaml # PNPM workspace config
└── README.md
- Passwords hashed with bcrypt (configurable rounds)
- Never returned in API responses (select: false)
- Minimum 8 characters required
- Current password required for password changes
- Database-backed sessions (scalable to Redis)
- Automatic cleanup of expired sessions
- HttpOnly, Secure, SameSite cookies
- 7-day expiration (configurable)
- Structured error responses
- Production-safe messages (no stack traces in prod)
- Proper HTTP status codes
- Detailed logging for debugging
- All entities support soft delete
- Deleted data retained in database with
deletedAttimestamp - Cascade soft deletes through relationships
- Can be restored if needed
- User authentication and authorization
- Password hashing and security
- Session management
- CRUD operations for all resources
- Soft deletes
- Pagination
- Database indexes
- Error handling
- Logging
- API documentation
- Security headers
- Rate limiting
- CORS
- Email verification
- Password reset via email
- Role-based access control (RBAC)
- Coach/athlete relationships
- Workout logging and tracking
- Progress analytics and charts
- File uploads (exercise videos/images)
- Search and filtering
- Database migrations
- GraphQL API (optional)
- Real-time updates (WebSockets)
- Mobile app integration
- Social features (sharing, comments)
- Nutrition tracking
- Calendar integration
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write tests for new features
- Follow existing code style (ESLint/Prettier)
- Update documentation
- Use conventional commits
- Keep PRs focused and small
[Your License Here]
- Issues: GitHub Issues
- Email: [Your Email]
- Documentation:
/docsendpoint
- Built with TypeORM
- Express.js
- PostgreSQL
- TypeScript
- React
- Vite