A modern, production-ready project management API built with FastAPI, PostgreSQL, SQLAlchemy, and JWT authentication.
- User Management: User registration and authentication with JWT tokens
- Project Management: Create and manage projects with ownership tracking
- Task Management: Full task lifecycle management with status and priority tracking
- Team Collaboration: Project members with role-based access control
- Activity Tracking: Audit logs for all project activities
- Comments & Discussions: Task-level comments and discussions
- Tagging System: Flexible tagging system for tasks
- Notifications: User notification system with metadata support
- Framework: FastAPI - Modern async Python web framework
- Database: PostgreSQL with SQLAlchemy ORM
- Authentication: JWT tokens with 7-day expiration
- Migrations: Alembic for schema versioning
- Testing: pytest with comprehensive test coverage
- Password Hashing: bcrypt for secure password storage
- Python 3.12+
- PostgreSQL 15+
- Virtual environment (venv, conda, or similar)
git clone <repository-url>
cd fastapi_pm_tool_backendpython -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activatepip install -r requirements.txtCreate a .env file in the project root:
# Database Configuration
DATABASE_USER=pm_tool_user
DATABASE_PASSWORD=your_secure_password
DATABASE_HOST=localhost
DATABASE_PORT=5432
# JWT Configuration
JWT_SECRET_KEY=your_super_secret_key_change_this_in_production
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_DAYS=7Important: Generate a strong JWT secret key:
python -c "import secrets; print(secrets.token_urlsafe(32))"# Create the postgres user and production database
sudo -u postgres psql
CREATE USER pm_tool_user WITH PASSWORD 'your_password';
ALTER USER pm_tool_user CREATEDB;
CREATE DATABASE pm_tool OWNER pm_tool_user;
CREATE DATABASE pm_tool_test OWNER pm_tool_user;
\q.venv/bin/alembic upgrade headThis creates all tables and indexes in the pm_tool database.
.venv/bin/uvicorn app.main:app --reloadThe API will be available at http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
You can also run the application with Docker for a consistent development environment:
docker-compose up --buildThis will:
- Start a PostgreSQL database container
- Run all database migrations automatically
- Start the FastAPI server on port 8000
The database will be available at localhost:5432 and the API at http://localhost:8000.
Railway is the easiest option for deploying this FastAPI application with PostgreSQL.
Setup:
- Sign up at railway.app
- Connect your GitHub repository
- Create a new project and select "Deploy from GitHub"
- Select this repository
- Railway will automatically detect FastAPI and create a Dockerfile
- Add PostgreSQL service:
- Click "Add Service" → "PostgreSQL"
- Railway will configure database credentials automatically
- Set environment variables in Railway dashboard:
JWT_SECRET_KEY=<generate-strong-secret> JWT_ALGORITHM=HS256 JWT_ACCESS_TOKEN_EXPIRE_DAYS=7 CORS_ORIGINS=https://your-frontend-domain.com - Deploy!
Railway will automatically:
- Build the Docker image
- Run database migrations
- Start the application
- Provide a public URL
Cost: Railway offers a free tier with generous limits.
Deploy on any VPS (DigitalOcean, AWS, Linode, etc.):
Build and Push to Registry:
# Build image
docker build -t pm-tool-backend:latest .
# Tag for registry (example: Docker Hub)
docker tag pm-tool-backend:latest yourusername/pm-tool-backend:latest
# Push to registry
docker push yourusername/pm-tool-backend:latestOn VPS:
# Pull image
docker pull yourusername/pm-tool-backend:latest
# Create docker-compose.yml with production settings
docker-compose -f docker-compose.production.yml up -dFor a traditional VPS without Docker:
# On server
git clone https://github.com/ericadev/pm_tool_fastapi.git
cd pm_tool_fastapi
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Run migrations
alembic upgrade head
# Start with production server (Gunicorn)
pip install gunicorn
gunicorn -w 4 -k uvicorn.workers.UvicornWorker app.main:app --bind 0.0.0.0:8000Use a reverse proxy (Nginx) for SSL/TLS termination and load balancing.
Ensure these are set on your deployment platform:
DATABASE_USER=pm_tool_user
DATABASE_PASSWORD=<strong-password>
DATABASE_HOST=<database-host>
DATABASE_PORT=5432
DATABASE_NAME=pm_tool
JWT_SECRET_KEY=<generate-with: python -c "import secrets; print(secrets.token_urlsafe(32))">
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_DAYS=7
CORS_ORIGINS=https://your-frontend-domain.com,https://www.your-frontend-domain.comThe application includes a root endpoint for health checks:
curl https://your-api-domain.com/
# Response: {"message": "Hello World"}This can be used by load balancers and monitoring services.
POST /users/- Register a new userPOST /users/login- Login and get JWT token
GET /projects/- Get all projectsPOST /projects/- Create a new project (requires authentication)
See the OpenAPI documentation at /docs for complete endpoint details.
The application includes the following main tables:
| Table | Purpose |
|---|---|
User |
User accounts and authentication |
Project |
Projects with ownership tracking |
ProjectMember |
Project team members with roles |
Task |
Project tasks with status and priority |
Comment |
Task comments and discussions |
Activity |
Audit log of all project activities |
Tag |
Reusable tags for tasks |
TaskTag |
Task-tag relationships |
Notification |
User notifications |
.venv/bin/pytest tests/ -v.venv/bin/pytest tests/test_auth.py -v.venv/bin/pytest tests/ --cov=app --cov-report=htmlTests automatically use the pm_tool_test database and clean up after each test. Your production database (pm_tool_dev) is never modified.
- Register:
POST /users/with email and password - Login:
POST /users/loginwith email and password to receive JWT token - Access Protected Routes: Include the token in Authorization header:
Authorization: Bearer <your_token_here> - Token Validation: JWT tokens are valid for 7 days from creation
Example:
curl -X POST "http://localhost:8000/users/login" \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"password123"}'
# Response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer"
}
# Use token to access protected routes:
curl -X GET "http://localhost:8000/projects/" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."fastapi_pm_tool_backend/
├── alembic/ # Database migrations
│ ├── versions/ # Migration files
│ └── env.py # Migration configuration
├── app/
│ ├── dependencies/ # Dependency injection (auth, etc.)
│ ├── models.py # SQLAlchemy ORM models
│ ├── database.py # Database configuration
│ ├── main.py # FastAPI app setup
│ ├── routes/ # API route handlers
│ │ ├── users.py
│ │ └── projects.py
│ ├── schemas/ # Pydantic schemas (validation)
│ │ ├── user.py
│ │ ├── project.py
│ │ └── auth.py
│ └── utils/ # Utility functions
│ ├── jwt.py # JWT token utilities
│ └── security.py # Password hashing
├── tests/ # Test suite
│ ├── conftest.py # Pytest configuration and fixtures
│ └── test_auth.py # Authentication tests (18 tests)
├── .env # Environment variables (git ignored)
├── requirements.txt # Python dependencies
└── README.md # This file
-
Create a feature branch:
git checkout -b feature/your-feature-name
-
Implement your feature with tests
-
Run tests to ensure nothing breaks:
.venv/bin/pytest tests/ -v
-
Create a pull request to merge into main
- Modify SQLAlchemy models in
app/models.py - Generate migration:
.venv/bin/alembic revision --autogenerate -m "Description of changes" - Review the generated migration
- Apply migration:
.venv/bin/alembic upgrade head
- ✅ Passwords are hashed with bcrypt before storage
- ✅ JWT tokens expire after 7 days
- ✅ Bearer token validation on protected endpoints
- ✅ Email uniqueness enforced at database level
- ✅ User input validated with Pydantic schemas
⚠️ Use HTTPS in production (configure in reverse proxy)⚠️ KeepJWT_SECRET_KEYsecure and private⚠️ Use environment-specific configurations for dev/staging/production
- Database indexes on frequently queried columns:
- User email (unique)
- Project owner ID
- Task status and project
- Activity creation time
- Connection pooling for database efficiency
- Async/await for non-blocking I/O operations
psycopg2.OperationalError: connection to server failed
Solution: Ensure PostgreSQL is running and credentials are correct in .env
KeyError: 'JWT_SECRET_KEY'
Solution: Add JWT_SECRET_KEY to .env file
alembic.util.exc.CommandError: Can't locate revision identified by...
Solution: Check migration history with alembic history and verify alembic.ini configuration
psycopg2.OperationalError: database "pm_tool_test" does not exist
Solution: Create test database:
sudo -u postgres psql -c "CREATE DATABASE pm_tool_test OWNER pm_tool_user;"- Create a feature branch from main
- Make your changes with tests
- Ensure all tests pass:
pytest tests/ -v - Create a pull request with a clear description
- Get code review before merging
[Add your license here]
For issues, questions, or suggestions, please create an issue in the repository.
Last Updated: January 2026 Status: Production Ready ✅