A full-stack URL shortener: Node/Express/MongoDB backend, React/Tailwind frontend.
- JWT auth (register, login, refresh, logout) with hashed passwords and revocable refresh tokens
- Shorten URLs with custom alias or random code, with collision + duplicate detection
- Fast redirect endpoint with click logging (IP, user agent, referrer, timestamp)
- Per-URL analytics: total clicks, unique visitors, daily click chart, recent clicks
- URL expiration by date or max click count
- Search/filter/pagination on the URL list
- Security: Helmet, CORS, rate limiting, Mongo sanitization, XSS cleaning, secure cookies
- Centralized error handling with custom ApiError class
- Winston + Morgan logging
- Jest + Supertest integration tests (in-memory MongoDB)
- Swagger UI at
/api-docs - Dockerfiles for client + server, docker-compose for the full stack
Email verification, forgot/reset password, QR codes, bulk CSV shortening, admin panel, geo/device analytics (needs a GeoIP + UA-parsing library), Redis caching, Socket.io real-time analytics, PWA/offline support, webhook/API-key system, PDF/Excel export. Ask and these can be added incrementally on top of this base.
server/ Express API (MVC-ish: models, controllers, routes, middleware, validators)
client/ React + Vite + Tailwind frontend
docker-compose.yml
cd server
cp .env.example .env # then edit secrets/URI
npm install
npm run dev # starts on http://localhost:5000cd client
npm install
npm run dev # starts on http://localhost:5173, proxies /api to :5000cd server
npm testcp server/.env.example server/.env # edit secrets
docker compose up --build- Client: http://localhost:3000
- API: http://localhost:5000
- Swagger docs: http://localhost:5000/api-docs
| Variable | Description |
|---|---|
| PORT | API port (default 5000) |
| MONGO_URI | MongoDB connection string |
| JWT_ACCESS_SECRET / JWT_REFRESH_SECRET | JWT signing secrets — change these |
| JWT_ACCESS_EXPIRES / JWT_REFRESH_EXPIRES | Token lifetimes |
| CLIENT_URL | Frontend origin, used for CORS |
| BASE_URL | Public API base URL, used in Swagger |
| RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAX | General rate limit config |
POST /api/auth/register
POST /api/auth/login
POST /api/auth/refresh
POST /api/auth/logout
POST /api/url (auth required)
GET /api/url ?page=&limit=&search=&status=
GET /api/url/:id
PUT /api/url/:id
DELETE /api/url/:id
GET /:shortCode (public redirect)
GET /api/analytics/:id (auth required)
- Mongo connection refused: make sure MongoDB is running and
MONGO_URImatches (usemongoas host in docker-compose,localhostwhen running locally). - CORS errors:
CLIENT_URLin.envmust exactly match the frontend origin, including port. - 401 on every request: access tokens expire quickly (15m default); the axios instance in
client/src/api/axios.jsauto-refreshes using the httpOnly cookie — make sure cookies are enabled andwithCredentials: trueis set.