Skip to main content

LinkShort

🧪 View animated test results — 15/15 →

A production-ready URL shortener — FastAPI backend, JWT auth, click tracking, React 19 SPA, SQLite.

LinkShort (repository: FastAPI-url) is a complete URL-shortening service with a Python/FastAPI API, JWT-based authentication, per-user link management, click statistics, and a served single-page application frontend.

Stack: FastAPI (Python) · SQLAlchemy · SQLite · JWT (python-jose) · React 19 SPA (static files served by FastAPI) · pytest

Highlights

  • Full auth flow — register → login → JWT bearer tokens; /auth/me returns the current user
  • 6-character short codes — cryptographically random (secrets), with collision retry
  • Click tracking — every redirect increments the click counter
  • Link expiration — optional expires_in_seconds per link; expired links answer 410 Gone while stats stay viewable
  • Per-user URLs/urls/my lists your links; deletes are owner-scoped
  • 302 redirects/urls/r/{code} redirects to the target with a click recorded
  • Public stats/urls/{code}/stats returns target + click count
  • SPA serving — FastAPI mounts the built React app and falls back to index.html for client-side routing
  • CORS openallow_origins=["*"] for development

Tech stack

LayerTechnology
APIFastAPI (app = FastAPI(title="URL Shortener", version="1.0.0"))
ORMSQLAlchemy (Base.metadata.create_all on startup)
DatabaseSQLite
AuthJWT bearer tokens (python-jose)
Password hashingapp/auth.py (hashed, never stored plaintext)
FrontendReact 19 SPA in backend/static/
Testspytest (tests/test_api.py)

API surface

MethodPathAuthDescription
POST/auth/registerCreate account, receive JWT
POST/auth/loginAuthenticate, receive JWT
GET/auth/meBearerCurrent user profile
POST/urls/shortenBearerShorten a URL
GET/urls/myBearerList your URLs (newest first)
GET/urls/{code}/statsPublic click stats
DELETE/urls/{code}BearerDelete your URL (owner-scoped)
GET/urls/r/{code}302 redirect + click count
GET/healthLiveness probe

Repository layout

FastAPI-url/
├── app/
│ ├── main.py # FastAPI app, CORS, SPA serving
│ ├── auth.py # JWT creation/verification, password hashing
│ ├── database.py # SQLAlchemy engine + session
│ ├── models.py # User, URL models
│ ├── schemas.py # Pydantic schemas (UserCreate, Token, URLOut, URLStats)
│ ├── config.py # Settings
│ └── routers/
│ ├── auth_router.py # /auth/* endpoints
│ └── urls.py # /urls/* endpoints
├── backend/static/ # Built React SPA (served by FastAPI)
├── tests/
│ └── test_api.py # API integration tests
└── ...

Short-code generation

def gen_short() -> str:
chars = string.ascii_letters + string.digits
return ''.join(secrets.choice(chars) for _ in range(6))

6 characters from a 62-char alphabet ≈ 5.7×10¹⁰ combinations; collisions are checked and retried against the database.