A full-stack URL shortener. Paste a long link and get a short one, pick your own custom alias, track how many times it's clicked, and share it with a QR code.
▶ Live demo: https://zipurl-two.vercel.app
This started as one of my first full-stack projects in college (React + Flask + Firebase on Heroku). I rebuilt it in 2026 with a cleaner architecture, a real database layer, click analytics, and tests.
- Shorten any link: generates a random 6-character code, or use your own alias like
/my-portfolio - Click tracking: every redirect is counted, and you can see the total and the time of the last click
- QR codes: generated instantly for any short link
- Recent links: your last 10 links are saved in the browser, with one-click copy and live click counts
- Validation that protects users: only
http(s)URLs are accepted (nojavascript:links), aliases are checked for format and uniqueness, and reserved paths like/apican't be taken - Light and dark mode: follows your system setting
| Layer | Tech |
|---|---|
| Frontend | React 19, Vite, qrcode.react |
| Backend | Python, Flask 3 |
| Database | Postgres (Neon) in production, SQLite locally |
| Testing | pytest (19 tests) |
| Hosting | Vercel (static React build + Flask as a Python function) |
Browser ──POST /api/links──► Flask ──► SQLite (code, url, clicks, created_at)
Browser ──GET /<code>──────► Flask ──► increment clicks ──► 302 redirect to original URL
In development, Vite proxies /api calls to Flask. In production, Flask serves the built React app and the API from one server.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/links |
Body { "url": "...", "alias": "optional" }. Returns the new link (201), 400 if invalid, 409 if the alias is taken |
GET |
/api/links/<code> |
Stats for a link (URL, clicks, created, last click) |
GET |
/<code> |
Redirects to the original URL and counts the click |
GET |
/api/health |
Health check |
1. Start the API (Python 3.10+)
cd server
python -m venv .venv
.venv\Scripts\activate # macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
flask --app app run2. Start the client (Node 20+), in a second terminal
cd client
npm install
npm run devOpen http://localhost:5173.
cd server
pytestThe live version runs on Vercel with a Neon Postgres database:
vercel.jsonbuilds the React client intopublic/, which is served as static filesapi/index.pyexposes the Flask app as a Python function;/api/*and short-link paths are rewritten to it- The server uses Postgres automatically when
DATABASE_URLis set, and falls back to SQLite otherwise
Every push to main deploys automatically.
client/ React + Vite frontend
src/App.jsx Shortener form, result card, QR code, link history
src/api.js API helpers
api/index.py Vercel function entrypoint
server/ Flask backend
app.py Routes, validation, Postgres/SQLite storage
tests/ pytest suite