A production-ready full-stack URL shortener built to learn real backend engineering concepts like caching, analytics, database modelling, Docker, and deployment debugging.
- Shorten long URLs into compact links
- Fast redirects using Redis caching
- Click analytics with total click count
- Dockerized frontend, backend, database, and Redis
- Production deployment ready
- React (Vite)
- TypeScript
- Tailwind CSS
- Node.js
- Express.js
- TypeScript
- Prisma ORM
- PostgreSQL
- Redis
- Docker
- Docker Compose
- Render (deployment)
url-shortener/
├── backend/
│ ├── src/
│ │ ├── controllers/
│ │ ├── routes/
│ │ ├── lib/
│ │ │ ├── prisma.ts
│ │ │ └── redis.ts
│ │ ├── utils/
│ │ │ ├── generateSlug.ts
│ │ │ └── parseUserAgent.ts
│ │ └── index.ts
│ ├── prisma/
│ │ ├── schema.prisma
│ │ └── migrations/
│ ├── Dockerfile
│ └── package.json
│
├── frontend/
│ ├── src/
│ │ ├── App.tsx
│ │ ├── config.ts
│ │ └── main.tsx
│ ├── Dockerfile
│ └── package.json
│
├── docker-compose.yml
└── README.md
- PostgreSQL stores URLs and analytics data
- Redis caches slug → original URL mappings for fast redirects
- Prisma manages schema, migrations, and type-safe database access
- Express exposes APIs for URL creation, redirection, and analytics
- React frontend consumes backend APIs
- Docker Compose orchestrates all services locally
Redis is being used only for read optimization and never skips analytics logic.
model Url {
id String @id @default(uuid())
originalUrl String
slug String @unique
expiresAt DateTime?
createdAt DateTime @default(now())
clicks Click[]
}
model Click {
id String @id @default(uuid())
urlId String
device String?
browser String?
os String?
referrer String?
createdAt DateTime @default(now())
url Url @relation(fields: [urlId], references: [id])
}- User visits
/slug - Backend checks Redis cache
- Cache hit → instant redirect
- Cache miss → DB lookup → cache result
- Click analytics recorded
- User redirected to original URL
- Each redirect logs a click entry
- Analytics endpoint returns total click count
- Frontend fetches analytics on load and refresh
POST /api/shorten
Request:
{
"url": "https://example.com"
}Response:
{
"id": "uuid",
"originalUrl": "https://example.com",
"slug": "abc123",
"shortUrl": "https://backend-domain/abc123"
}GET /:slug
Redirects to the original URL and records analytics.
GET /api/analytics/:slug
Response:
{
"totalClicks": 5
}Run the entire stack locally:
docker compose up --buildServices:
- Frontend → http://localhost:5173
- Backend → http://localhost:4000
- PostgreSQL → localhost:5432
- Redis → localhost:6379
DATABASE_URL=postgres://postgres:postgres@postgres:5432/url_shortener
REDIS_HOST=redis
PORT=4000VITE_API_BASE_URL=https://your-backend-url- APIs tested using Postman and browser.
- Analytics verified using Prisma Studio.
- Network calls inspected via browser DevTools.
- Redis hit/miss behavior tested manually.
- Redis should optimize reads, not bypass business logic.
- Analytics endpoints must never be cached.
- Async DB writes must be awaited before responding.
- Frontend and backend origins must be handled explicitly.
- Docker simplifies multi-service development.
- Production debugging differs from local debugging.
This project includes tools to generate quantifiable metrics for your resume.
Run the included k6 script to simulate high traffic and measure latency.
# Install k6 (if not installed)
# winget install k6 (Windows)
# brew install k6 (Mac)
# Run the benchmark
k6 run scripts/load-test.jsExpected Results for Resume:
"Engineered a high-performance URL shortener handling 2,000+ requests/second with <50ms average latency using Redis caching."
The dashboard visualizes:
- Traffic Sources: Referrer tracking.
- User Demographics: Device, OS, and Browser breakdown.
- Real-time Stats: Instant click updates.
Sparsh Birla
GitHub: https://github.com/sparsh13b