Skip to content

Commit 82c5ded

Browse files
committed
Add migrations, frontend tests, and project cleanup
1 parent c083499 commit 82c5ded

31 files changed

Lines changed: 3653 additions & 667 deletions

.dockerignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ test.db
3232
# Build artifacts
3333
dist/
3434
build/
35+
frontend/dist/
36+
frontend/.vite/
37+
frontend/node_modules/
38+
coverage/
39+
frontend/coverage/
40+
htmlcov/
3541

3642
# OS
3743
.DS_Store

.env.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,11 @@ REDIS_URL=redis://redis:6379/0
1313
# Base URL for generated short links
1414
BASE_URL=http://127.0.0.1:8000
1515

16+
# Allowed frontend origins for CORS (comma-separated)
17+
CORS_ALLOW_ORIGINS=http://localhost:5173,https://url-shortener-frontend-av1x.onrender.com
18+
19+
# Automatically create database tables on startup
20+
AUTO_CREATE_SCHEMA=false
21+
1622
# App port
17-
PORT=8000
23+
PORT=8000

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ venv/
2727
# Coverage
2828
.coverage
2929
htmlcov/
30+
coverage/
31+
frontend/coverage/
3032

3133
# Local databases
3234
*.sqlite3

README.md

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# URL Shortener
22

3-
A production-style distributed URL shortener demonstrating caching, rate limiting, load balancing, and horizontal scalability. Built with a FastAPI backend, React frontend, PostgreSQL, Redis, Docker, and CI/CD (GitHub Actions).
3+
A production-style distributed URL shortener demonstrating caching, rate limiting, load balancing, and horizontal scalability. Built with a FastAPI backend, React frontend, PostgreSQL, Redis, Docker, and CI (GitHub Actions).
44

55
## Live Demo
66

@@ -17,11 +17,14 @@ A lightweight React frontend provides a simple interface for:
1717

1818
The frontend communicates with the deployed FastAPI backend via REST APIs, enabling end-to-end interaction with the distributed system.
1919

20+
When the backend returns structured error details, such as rate-limit responses, the frontend surfaces those messages in the UI.
21+
2022
## Deployment
2123

2224
- Deployed on Render (cloud platform)
2325
- Uses managed PostgreSQL as the persistent source of truth
2426
- Redis is used as a shared cache and coordination layer in distributed environments, with graceful fallback when unavailable
27+
- Reverse proxies should preserve `X-Forwarded-For` so Redis-backed rate limiting can identify individual clients correctly
2528
- Environment-based configuration enables seamless switching between local, Docker, and cloud deployments
2629
- Frontend deployed as a static site on Render, providing a user interface for interacting with backend APIs
2730

@@ -55,6 +58,7 @@ Key goals:
5558
- Stateless FastAPI services behind an Nginx load balancer
5659
- PostgreSQL as the single source of truth for durability and consistency
5760
- Redis used for shared caching and distributed rate limiting
61+
- Rate limiting uses the first `X-Forwarded-For` address when requests pass through a reverse proxy or load balancer
5862
- Horizontal scaling achieved via multiple stateless application replicas
5963
- Graceful degradation when Redis is unavailable
6064

@@ -66,7 +70,7 @@ Key goals:
6670
- Cache redirect lookups using Redis
6771
- Enforce request rate limits using Redis-backed distributed rate limiting
6872
- Run the full stack locally using Docker Compose
69-
- Validate system behavior with pytest and GitHub Actions CI
73+
- Validate backend behavior with pytest, frontend behavior with Vitest, and backend CI checks with GitHub Actions
7074
- Support horizontal scaling via stateless application instances behind a load balancer
7175
- Benchmark cache performance (miss vs. hit latency)
7276

@@ -76,14 +80,22 @@ To validate the effectiveness of Redis caching, redirect latency was measured fo
7680

7781
Run locally:
7882
```bash
79-
python scripts/benchmark_cache.py
83+
python3 scripts/benchmark_cache.py
84+
```
85+
86+
Optional environment overrides:
87+
```bash
88+
BENCHMARK_BASE_URL=http://127.0.0.1:8000 \
89+
BENCHMARK_ORIGINAL_URL=https://www.google.com \
90+
BENCHMARK_HIT_RUNS=20 \
91+
python3 scripts/benchmark_cache.py
8092
```
8193

8294
Example results:
8395

84-
- Cache miss latency: ~30–45 ms
85-
- Average cache hit latency: ~6 ms
86-
- Approximate speedup: ~5–7×
96+
- Cache miss latency: ~10–32 ms
97+
- Average cache hit latency: ~8–11 ms
98+
- Approximate speedup: ~1–3×
8799

88100
This demonstrates that Redis caching significantly reduces redirect latency and minimizes repeated database queries in read-heavy workloads.
89101

@@ -93,8 +105,8 @@ This demonstrates that Redis caching significantly reduces redirect latency and
93105
- Implemented PostgreSQL-backed persistence as the durable source of truth for URL mappings and analytics
94106
- Integrated Redis for shared caching and distributed rate limiting with graceful fallback when unavailable
95107
- Containerized and orchestrated multiple application instances using Docker Compose to simulate a distributed environment
96-
- Built automated test coverage with pytest to validate core workflows
97-
- Configured GitHub Actions CI to run tests on every push and pull request
108+
- Built automated test coverage with pytest for the backend and Vitest for the frontend
109+
- Configured GitHub Actions CI to run backend tests on every push and pull request
98110
- Introduced Nginx as a load balancer to distribute traffic across multiple FastAPI instances
99111
- Validated Redis caching effectiveness using benchmark measurements (cache miss vs. hit latency)
100112

@@ -105,10 +117,13 @@ This demonstrates that Redis caching significantly reduces redirect latency and
105117
- PostgreSQL
106118
- Redis
107119
- SQLAlchemy
120+
- Alembic
108121
- React
109122
- Docker / Docker Compose
110123
- Nginx
111124
- pytest
125+
- Vitest
126+
- React Testing Library
112127
- GitHub Actions
113128

114129
## Project Structure
@@ -130,7 +145,16 @@ app/ # FastAPI backend application
130145
schemas.py # Pydantic schemas
131146
utils.py # helper utilities
132147
148+
alembic/ # Alembic migration environment
149+
versions/ # migration revision files
150+
alembic.ini # Alembic configuration
151+
133152
frontend/ # React frontend (Vite, API integration)
153+
src/
154+
App.jsx # main frontend application
155+
App.test.jsx # frontend UI tests
156+
test/ # frontend test setup
157+
134158
nginx/ # Nginx configuration for load balancing
135159
tests/ # automated tests
136160
@@ -149,76 +173,160 @@ requirements.txt # backend dependencies
149173
- `DATABASE_URL` — PostgreSQL connection string
150174
- `REDIS_URL` — Redis connection string (optional)
151175
- `BASE_URL` — base URL for generated short links
176+
- `CORS_ALLOW_ORIGINS` — comma-separated frontend origins allowed to call the API
177+
- `AUTO_CREATE_SCHEMA` — enables automatic table creation on startup; defaults to enabled in development/test and disabled in production-style environments
152178
- `PORT` — application port
153179

154180
## Health Check
155181

156182
The service exposes a health check endpoint:
183+
157184
```http
158185
GET /health
159186
```
187+
160188
```bash
161189
curl http://127.0.0.1:8000/health
162190
```
163191

164192
## How to Run Locally
165193

166-
### Run Full Stack with Docker
194+
### Run Backend Stack with Docker
167195

168196
```bash
169197
docker compose up --build
170198
```
199+
171200
Open:
172201
- Backend API docs: http://127.0.0.1:8000/docs
173202
- Backend health: http://127.0.0.1:8000/health
174203

204+
The Docker Compose backend stack runs `python -m alembic upgrade head` before starting the FastAPI replicas, so a fresh database is migrated automatically.
205+
175206
### Run Backend Locally (Services in Docker)
176207

177208
- Start required services:
178209
```bash
179210
docker compose up -d db redis
180211
```
212+
181213
- Activate virtual environment:
182214
```bash
183215
source venv/bin/activate
184216
```
217+
185218
- Install dependencies:
186219
```bash
187-
pip install -r requirements.txt
220+
python3 -m pip install -r requirements.txt
188221
```
222+
223+
- Configure local environment:
224+
```bash
225+
export BASE_URL=http://127.0.0.1:8000
226+
```
227+
228+
For local runs, make sure `DATABASE_URL`, `REDIS_URL`, `BASE_URL`, and `AUTO_CREATE_SCHEMA` are set through your `.env` file or exported in the shell.
229+
230+
Example local backend environment:
231+
```bash
232+
export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/urlshortener
233+
export REDIS_URL=redis://localhost:6379/0
234+
export BASE_URL=http://127.0.0.1:8000
235+
export AUTO_CREATE_SCHEMA=false
236+
```
237+
238+
- Apply migrations:
239+
```bash
240+
python3 -m alembic upgrade head
241+
```
242+
189243
- Run backend:
190244
```bash
191-
uvicorn app.main:app --reload
245+
python3 -m uvicorn app.main:app --reload
192246
```
193247

248+
`BASE_URL` controls the short links returned by the API, so set it to the backend address you want clients to use.
249+
194250
### Run Frontend Locally
195251

196252
```bash
197253
cd frontend
198254
npm install
255+
export VITE_API_BASE_URL=http://127.0.0.1:8000
199256
npm run dev
200257
```
258+
201259
Open:
202260
Frontend: http://localhost:5173
203261

262+
`VITE_API_BASE_URL` tells the frontend which backend API to call in local development or deployment environments.
263+
204264
## Run Tests
205265

266+
- Backend tests:
267+
```bash
268+
python3 -m pytest -v
269+
```
270+
271+
- Frontend tests:
272+
```bash
273+
cd frontend
274+
npm run test:run
275+
```
276+
277+
The frontend uses Vitest and React Testing Library to cover core UI flows such as shorten success, stats fetch success, and backend error display.
278+
279+
GitHub Actions currently runs backend tests on every push and pull request. Frontend tests are available locally with `npm run test:run`.
280+
281+
## Database Migrations
282+
283+
This project uses Alembic for schema migrations.
284+
285+
- Apply the latest migrations:
206286
```bash
207-
pytest -v
287+
python3 -m alembic upgrade head
208288
```
209289

290+
- Create a new migration after changing models:
291+
```bash
292+
python3 -m alembic revision --autogenerate -m "describe change"
293+
```
294+
295+
- Check the current revision:
296+
```bash
297+
python3 -m alembic current
298+
```
299+
300+
- For normal development and deployment, prefer migrations with:
301+
```bash
302+
AUTO_CREATE_SCHEMA=false
303+
```
304+
305+
`AUTO_CREATE_SCHEMA=true` is still available for quick demo or prototyping workflows, but `AUTO_CREATE_SCHEMA=false` should be the default once the schema is managed by Alembic.
306+
210307
## Seed Sample Data
211308

212309
```bash
213-
python -m scripts.seed
310+
python3 -m alembic upgrade head
311+
python3 -m scripts.seed
214312
```
215313

314+
Run the migration step first when seeding a fresh database.
315+
216316
## Quick Demo Flow
217317

218-
- Start the full stack:
318+
- Start the backend stack:
219319
```bash
220320
docker compose up --build
221321
```
322+
323+
- In another terminal, start the frontend:
324+
```bash
325+
cd frontend
326+
export VITE_API_BASE_URL=http://127.0.0.1:8000
327+
npm run dev
328+
```
329+
222330
- Open the frontend UI: http://localhost:5173
223331
- Enter a URL (e.g., https://www.google.com) and generate a short link
224332
- Open the returned short URL in your browser to verify redirection
@@ -333,4 +441,4 @@ Nginx Load Balancer
333441
- Enhance rate limiting with sliding window or token bucket algorithms
334442
- Implement custom aliases and expiration policies
335443
- Build analytics aggregation pipeline for high-volume traffic
336-
- Deploy multi-instance setup to the cloud using container orchestration (e.g., Kubernetes)
444+
- Deploy multi-instance setup to the cloud using container orchestration (e.g., Kubernetes)

0 commit comments

Comments
 (0)