A simple and scalable URL Shortener application built using FastAPI and SQLite, following clean architecture principles.
- Shorten long URLs into unique short links
- Redirect short URLs to original URLs
- Simple HTML UI using Jinja2 templates
- Clean architecture (Router → Service → Repository → DB)
- SQLite database (lightweight & easy setup)
This project follows a layered architecture:
Client (HTML Form)
↓
Router Layer (FastAPI)
↓
Service Layer (Business Logic)
↓
Repository Layer (DB Access)
↓
SQLite Database
- Separation of concerns
- Easy to scale and maintain
- Clean and testable code
- Reusable components
url_shortener/
├── app/
│ ├── main.py
│ ├── database.py
│ ├── models.py
│ ├── schemas.py
│ ├── services.py
│ ├── repositories.py
│ ├── routers/
│ │ └── url_router.py
│ └── templates/
│ └── index.html
├── docs/
│ ├── context/
│ │ └── repository-context.md
│ ├── deployment/
│ │ ├── docker-desktop-kubernetes.md
│ │ └── environment-promotion.md
│ └── learning/
│ └── kubernetes-break-fix.md
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── publish-dev-image.yml
│ └── publish-stage-image.yml
├── k8s/
│ ├── base/
│ │ ├── deployment.yaml
│ │ ├── kustomization.yaml
│ │ └── service.yaml
│ └── environments/
│ ├── dev/
│ ├── stage/
│ └── prod/
├── scripts/
│ ├── deploy-dev.sh
│ ├── deploy-prod.sh
│ ├── deploy-stage.sh
│ └── start.sh
├── tests/
├── requirements.txt
└── README.md
- Backend: FastAPI
- Database: SQLite
- ORM: SQLAlchemy
- Frontend: HTML (Jinja2 Templates)
- Server: Uvicorn
- Quality: pytest, coverage, Ruff
- Deployment: Docker, Kustomize, Docker Desktop Kubernetes, GitHub Actions, Docker Hub, Argo CD
git clone <your-repo-url>
cd url_shortenercurl -Ls https://astral.sh/uv/install.sh | sh
uv --version
uv venv
source .venv/bin/activatepip install -r requirements.txt./scripts/start.shOpen in browser:
http://127.0.0.1:8000
The deployment scripts are for manual local testing against Docker Desktop
Kubernetes. They build the local image url-shortener:dev, apply the selected
Kustomize overlay, and override the Deployment to use the local Docker Desktop
image.
./scripts/deploy-dev.sh
kubectl port-forward svc/url-shortener 30080:80 -n url-shortener-devStage and production local overlays are available through:
./scripts/deploy-stage.sh
./scripts/deploy-prod.shLocal environment endpoints:
dev: http://localhost:30080
stage: http://localhost:30081
prod: http://localhost:30082
The dev and stage branches are deployed through Docker Hub and Argo CD.
When a commit lands on dev, .github/workflows/publish-dev-image.yml:
- Builds the Docker image.
- Tags it as
sunlnx/url-shortener:dev_<short_commit_id>. - Pushes it to Docker Hub.
- Updates
k8s/environments/dev/kustomization.yamlwith the new tag. - Commits the tag update back to
dev.
Argo CD watches the dev branch and deploys the updated image to the
url-shortener-dev namespace.
When a commit lands on stage, .github/workflows/publish-stage-image.yml
performs the same flow with the stage_<short_commit_id> tag, updates
k8s/environments/stage/kustomization.yaml, and lets Argo CD deploy the stage
overlay to the url-shortener-stage namespace.
Required GitHub repository secrets:
DOCKER_HUB_LOGIN
DOCKER_HUB_TOKEN
Production is represented by the master branch in this repository. Normal
promotion flow is:
feature/<issue-number>-short-description -> dev
dev -> stage
stage -> master
Production deployment still requires explicit manual approval before running
./scripts/deploy-prod.sh.
Run the local test suite:
.venv/bin/python -m pytest -qRun linting, formatting checks, and coverage:
.venv/bin/python -m ruff check .
.venv/bin/python -m ruff format --check .
.venv/bin/python -m pytest -q --cov=app --cov-report=term-missing --cov-report=xmlGitHub Actions runs .github/workflows/ci.yml on pushes and PRs. The workflow
runs Ruff, pytest with coverage, uploads coverage artifacts, writes a job
summary, and posts a sticky coverage comment on PRs.
- User enters a long URL in the UI
- Request is sent to
/shorten - Service generates a unique short code
- Data is stored in SQLite
- Short URL is displayed
- Clicking short URL redirects to original URL
GET /
- Displays HTML form
POST /shorten
- Accepts form input
- Returns shortened URL
GET /{short_code}
- Redirects to original URL
Table: url_mappings
| Column | Type | Description |
|---|---|---|
| id | Integer | Primary key |
| original_url | String | Long URL |
| short_code | String | Unique short identifier |
| created_at | DateTime | Timestamp |
Handles HTTP requests and responses.
Contains business logic:
- Generate short codes
- Handle duplicates
- Validate logic
Handles all database interactions.
SQLite database using SQLAlchemy ORM.
https://www.google.com/search?q=fastapi+url+shortener
http://127.0.0.1:8000/aB12Cd
Clicking the short URL redirects to the original URL.
- Custom short URLs
- Expiry for links
- Click analytics
- Redis caching
- Rate limiting
- Authentication & user accounts
- Production-ready persistent storage or an external database
- Ingress, TLS, and cloud Kubernetes deployment
Additional project notes are organized under docs/:
- Repository context
- Docker Desktop Kubernetes deployment
- Environment promotion flow
- Kubernetes break/fix learning path
Sunil SRE | DevOps | Automation Engineer