Welcome to the Mini URL Shortener project! This is the first milestone of building a highly scalable, low-latency URL shortener system.
In this first milestone, we have built a functional core utilizing a Client-Server architecture and a Stateless FastAPI application backed by a simple SQLite Database.
- Create Short URL: Convert a long URL into a random 6-character short code.
- Redirect to Original URL: Automatically redirect users from
/{short_code}to the original URL. - Track Clicks: Record every time someone uses a short code.
- Get Analytics: See how many times a particular short link has been clicked.
- Backend Framework: FastAPI (Python)
- Database: PostgreSQL (Upgraded in Milestone 2)
- ORM: SQLAlchemy
- Validation: Pydantic
- Containerization: Docker & Docker Compose
The project is structured in a modular way so it can easily grow in future milestones:
url_shortner/
├── app/
│ ├── main.py # Application entry point
│ ├── api/
│ │ └── routes.py # API endpoints (/shorten, /{code}, /analytics)
│ ├── core/
│ │ └── config.py # Environment variables and settings
│ ├── db/
│ │ ├── database.py # Database connection setup
│ │ └── models.py # SQLAlchemy tables (URL, Click)
│ ├── schemas/
│ │ └── schemas.py # Request & Response Pydantic validation
│ └── services/
│ └── url_service.py # Core business logic
├── Dockerfile # Instructions to build the Docker image
├── docker-compose.yml # Easy way to run the service locally
└── requirements.txt # Python dependencies
If you have Docker Desktop installed, you can start the entire application with one command.
-
Open your terminal in the project directory.
-
Run the following command:
docker-compose up --build
-
The API will now be running at
http://localhost:8000.
(Note: A PostgreSQL container will be spun up automatically. Database files are persisted via Docker named volume postgres_data).
If you prefer to run it using standard Python on your machine:
-
Create a virtual environment and activate it:
python -m venv venv venv\Scripts\activate # On Windows
-
Copy the environment template and set up your PostgreSQL database connection:
copy .env.example .env # Open .env and adjust the DATABASE_URL if needed (must point to a running PostgreSQL server) -
Install the dependencies:
pip install -r requirements.txt
-
Start the server:
uvicorn app.main:app --reload
Make sure the server has run at least once to initialize the tables, then:
python view_db.pyFastAPI automatically generates an interactive documentation page. Once the server is running, simply go to:
-
Method:
POST -
Path:
/shorten -
Body:
{ "long_url": "https://www.google.com" } -
Response:
{ "short_code": "aB3dE5", "long_url": "https://www.google.com/" }
- Method:
GET - Path:
/{short_code}(e.g.,http://localhost:8000/aB3dE5) - Behavior: This won't return JSON. It will issue an HTTP 302 redirect and take your browser directly to the
long_url.
-
Method:
GET -
Path:
/analytics/{short_code}(e.g.,/analytics/aB3dE5) -
Response:
{ "short_code": "aB3dE5", "total_clicks": 1 }
To ensure the system can handle concurrent requests and maintain low latency, a load testing script is included.
# Make sure your server is running, then execute:
python load_test.pyThe script will automatically:
- Create a short URL.
- Spin up 50 concurrent users.
- Send 500 requests to the redirect endpoint.
- Print latency metrics (min, max, average) and Requests Per Second (Req/s) to the terminal.
- Save the final report to
load_test_result.txtin the root folder.
In the upcoming milestones, we will address scalability and performance:
- Migrating to PostgreSQL.
- Implementing Base62 encoding to prevent collision risks.
- Adding a Redis Cache to optimize the read-heavy redirect path.
- Making click-tracking eventually consistent (e.g., using background tasks).
- Problem: Running the load test (
load_test.py) populated the development/production database (shortener.dbordata/shortener.db) with dummy URLs and thousands of clicks, which is bad practice and pollutes data. - Resolution: Updated
load_test.pywith an automatic database cleanup mechanism. After running the load test, the script:- Identifies the active SQLite database file location (
shortener.dbordata/shortener.db). - Directly connects to the SQLite database.
- Finds the created test short code and deletes all its click records.
- Deletes the test short code itself, leaving the database exactly in its original clean state.
- Identifies the active SQLite database file location (