Ticket-API is a backend API for a portfolio-oriented ticketing and incident management system. It is designed to demonstrate practical backend, database, testing, containerization, and cloud-readiness skills for junior backend, cloud, and platform engineering roles.
The project is built with FastAPI, SQLAlchemy, PostgreSQL, Alembic, Docker Compose, pytest, and GitHub Actions CI.
Ticket-API provides a clean backend foundation for managing users, tickets, and incidents.
Current functionality includes:
- Versioned FastAPI routes under
/api/v1 - Health check endpoint
- Basic error handling
- SQLAlchemy domain models
- PostgreSQL-ready database configuration
- Alembic database migrations
- Docker-based local development with PostgreSQL
- Basic User CRUD endpoints
- Basic Ticket CRUD endpoints
- Automated tests with
pytest - GitHub Actions CI workflow
This project is being built to demonstrate practical backend and cloud-readiness skills, including:
- Python backend development with FastAPI
- Clean API structure and versioned routing
- Request and response validation with Pydantic
- SQLAlchemy ORM models and relationships
- PostgreSQL persistence readiness
- Alembic database schema migrations
- Docker and Docker Compose local development
- Automated testing with
pytest - Continuous Integration with GitHub Actions
- Clear documentation and professional Git workflow
- Preparation for future cloud deployment
- Python: main programming language
- FastAPI: backend API framework
- Uvicorn: ASGI server for running FastAPI
- Pydantic / pydantic-settings: data validation and application configuration
- SQLAlchemy: ORM for database models and sessions
- PostgreSQL: relational database
- psycopg: PostgreSQL driver
- Alembic: database migrations
- Docker: containerized application runtime
- Docker Compose: local API + database orchestration
- pytest: automated testing
- httpx / TestClient: API endpoint testing
- GitHub Actions: CI workflow
For local development:
- Python 3.11+
pip
For Docker-based local development:
- Docker
- Docker Compose
Create and activate a virtual environment:
python -m venv .venv
.venv\Scripts\activateInstall the project locally:
pip install -e .Install testing dependencies if needed:
pip install pytest httpxuvicorn app.main:app --reloadThe API will be available at:
http://127.0.0.1:8000/docshttp://127.0.0.1:8000/redochttp://127.0.0.1:8000/api/v1/health
Build and start the FastAPI and PostgreSQL containers:
docker compose up --buildThe API will be available at:
http://127.0.0.1:8000/docshttp://127.0.0.1:8000/redochttp://127.0.0.1:8000/api/v1/health
The PostgreSQL container is exposed locally on port 5432.
Stop the containers:
docker compose downStop the containers and remove the local PostgreSQL volume:
docker compose down -vDocker Compose passes this database URL to the API container:
postgresql+psycopg://ticket_user:ticket_password@db:5432/ticket_api
The application does not create tables automatically. Use Alembic to apply database migrations.
Start the PostgreSQL container:
docker compose up -d dbApply migrations:
.venv\Scripts\alembic.exe upgrade headAlternative:
python -m alembic upgrade headCreate a new migration after changing SQLAlchemy models:
.venv\Scripts\alembic.exe revision --autogenerate -m "describe change"Alembic reads DATABASE_URL from the application settings.
Use this host depending on where the command runs:
- From Windows/local terminal:
localhost - From inside Docker Compose:
db
To inspect tables:
docker compose exec db psql -U ticket_user -d ticket_api -c "\dt"Expected tables after migrations:
users
tickets
incidents
alembic_version
Expected response:
{
"status": "ok",
"service": "ticket-api",
"version": "0.1.0"
}This endpoint verifies that the API is running correctly.
Creates a user.
Example request:
{
"email": "requester@example.com",
"full_name": "Test Requester",
"hashed_password": "fake-hash",
"role": "requester",
"is_active": true
}Example response:
{
"id": 1,
"email": "requester@example.com",
"full_name": "Test Requester",
"role": "requester",
"is_active": true,
"created_at": "2026-05-28T20:00:00"
}hashed_password is accepted only because authentication and real password hashing are not implemented yet. It is not returned in API responses.
Returns all users.
Returns a user by ID.
If the user does not exist, the API returns:
{
"error": {
"message": "User not found."
}
}Partially updates a user.
Example request:
{
"role": "agent"
}If an email already belongs to another user, the API returns HTTP 409 Conflict:
{
"error": {
"message": "Email already exists."
}
}Creates a ticket.
A valid requester_id is required. assignee_id is optional, but if provided it must reference an existing user.
Example request:
{
"title": "Cannot access dashboard",
"description": "The dashboard returns an error after login.",
"priority": "high",
"requester_id": 1,
"assignee_id": 2
}Example response:
{
"id": 1,
"title": "Cannot access dashboard",
"description": "The dashboard returns an error after login.",
"status": "open",
"priority": "high",
"requester_id": 1,
"assignee_id": 2,
"created_at": "2026-05-28T20:00:00",
"updated_at": null
}If the requester does not exist:
{
"error": {
"message": "Requester not found."
}
}If the assignee does not exist:
{
"error": {
"message": "Assignee not found."
}
}Returns all tickets.
Returns a ticket by ID.
If the ticket does not exist:
{
"error": {
"message": "Ticket not found."
}
}Partially updates a ticket.
Example request:
{
"status": "in_progress",
"assignee_id": 2
}After starting the API and applying migrations:
- Open Swagger:
http://127.0.0.1:8000/docs
- Create a requester:
POST /api/v1/users
- Create an agent:
POST /api/v1/users
- Create a ticket using the requester ID:
POST /api/v1/tickets
- Assign or update the ticket using the agent ID:
PATCH /api/v1/tickets/{ticket_id}
- List tickets:
GET /api/v1/tickets
The project includes automated tests using pytest.
Run the tests locally:
.venv\Scripts\python.exe -m pytestAlternative:
python -m pytestCurrent test coverage includes:
- Health endpoint behavior
- SQLAlchemy model metadata registration
- PostgreSQL database URL configuration
- Basic Ticket CRUD behavior
- Ticket error handling for missing tickets, requester, and assignee
- Basic User CRUD behavior
- User error handling for missing users and duplicate emails
- Ensuring
hashed_passwordis not exposed in user responses
Current test files:
tests/test_health.py
tests/test_models.py
tests/test_database_config.py
tests/test_tickets.py
tests/test_users.py
Current expected result:
18 passed
The project includes a GitHub Actions CI workflow located at:
.github/workflows/ci.yml
The workflow runs automatically on:
- Pushes to
main - Pushes to branches matching
feat/** - Pull requests targeting
main
The CI pipeline performs the following steps:
- Checks out the repository.
- Sets up Python.
- Upgrades
pip. - Installs the project and test dependencies.
- Runs the test suite with
pytest.
This helps verify that the project continues to work after each change.
app/
api/
routes/
health.py
tickets.py
users.py
router.py
core/
config.py
errors.py
crud/
tickets.py
users.py
db/
base.py
session.py
models/
incident.py
ticket.py
user.py
schemas/
health.py
ticket.py
user.py
main.py
alembic/
env.py
script.py.mako
versions/
20260523_0001_create_initial_tables.py
tests/
test_database_config.py
test_health.py
test_models.py
test_tickets.py
test_users.py
.github/
workflows/
ci.yml
Dockerfile
docker-compose.yml
alembic.ini
pyproject.toml
README.md
The API includes consistent JSON error handling for:
404 Not Found409 Conflict422 Validation Error500 Internal Server Error
Example:
{
"error": {
"message": "Ticket not found."
}
}Completed:
- FastAPI project skeleton
- Versioned API routing
- Health check endpoint
- Basic error handling
- Automated testing with
pytest - GitHub Actions CI workflow
- SQLAlchemy domain models
- PostgreSQL database configuration
- SQLAlchemy engine and session setup
- Dockerfile and Docker Compose local development setup
- Alembic migration setup
- Initial database migration for users, tickets, and incidents
- Basic Ticket CRUD endpoints
- Basic User CRUD endpoints
The project intentionally does not include these features yet:
- Authentication
- Login endpoint
- JWT access tokens
- Password hashing
- Role-based authorization
- DELETE endpoints
- Pagination
- Filtering
- Incident CRUD endpoints
- Cloud deployment configuration
Recommended next phase:
- Add basic Incident CRUD endpoints.
Future phases may include:
- Password hashing and authentication
- JWT-based login
- Role-based authorization
- Pagination and filtering
- DELETE behavior
- CI/CD improvements
- Cloud deployment