A full-stack URL shortener with authentication, analytics, QR codes, admin panel, Redis caching, BullMQ background jobs, and more.
Backend: Node.js, Express, MongoDB (Mongoose), Redis (ioredis), BullMQ, nanoid, JWT, bcryptjs, QRCode
Frontend: React 18, Vite, Tailwind CSS, Recharts, React Hook Form, React Router v6, Axios
urlshortener/
├── backend/
│ ├── src/
│ │ ├── config/ # db.js, redis.js
│ │ ├── controllers/ # authController, urlController, adminController
│ │ ├── middleware/ # auth.js, rateLimiter.js, errorHandler.js
│ │ ├── models/ # User.js, Url.js
│ │ ├── routes/ # auth.js, urls.js, admin.js, redirect.js
│ │ ├── services/ # authService, urlService, adminService
│ │ ├── jobs/ # clickQueue.js (BullMQ)
│ │ ├── utils/ # jwt.js, validators.js
│ │ ├── app.js
│ │ └── server.js
│ ├── .env.example
│ └── package.json
└── frontend/
├── src/
│ ├── components/ # layout, dashboard, admin, ui
│ ├── context/ # AuthContext.jsx
│ ├── pages/ # Home, Login, Register, Dashboard, Analytics, Admin
│ ├── services/ # api.js (axios + token refresh)
│ └── main.jsx
├── index.html
└── package.json
cd backend
cp .env.example .env
# Edit .env with your MongoDB URI, Redis URL, and JWT secrets
npm install
npm run devcd frontend
npm install
npm run devFrontend runs on http://localhost:3000, backend on http://localhost:5000.
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 5000 |
MONGODB_URI |
MongoDB connection string | — |
REDIS_URL |
Redis connection string | redis://localhost:6379 |
BASE_URL |
Public URL of backend | http://localhost:5000 |
JWT_ACCESS_SECRET |
Secret for access tokens | — |
JWT_REFRESH_SECRET |
Secret for refresh tokens | — |
JWT_ACCESS_EXPIRES_IN |
Access token TTL | 15m |
JWT_REFRESH_EXPIRES_IN |
Refresh token TTL | 7d |
RATE_LIMIT_MAX_AUTH |
Max auth attempts per 15 min | 10 |
RATE_LIMIT_MAX_SHORTEN |
Max shorten requests per min | 20 |
CLIENT_URL |
Frontend origin for CORS | http://localhost:3000 |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register user |
| POST | /api/auth/login |
Login, returns JWT pair |
| POST | /api/auth/refresh |
Refresh access token |
| POST | /api/auth/logout |
Invalidate refresh token |
| GET | /api/auth/me |
Get current user |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/urls |
Create short URL |
| GET | /api/urls/my |
Get current user's links |
| GET | /api/urls/:code/analytics |
Get analytics |
| GET | /api/urls/:code/qr |
Get QR code |
| PUT | /api/urls/:code |
Update link |
| DELETE | /api/urls/:code |
Delete link |
| Method | Endpoint | Description |
|---|---|---|
| GET | /:code |
Redirect to original URL |
For password-protected links, send the password in the x-link-password header or ?p= query param.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/stats |
System statistics |
| GET | /api/admin/links |
All links (paginated) |
| DELETE | /api/admin/links/:code |
Delete any link |
| GET | /api/admin/users |
All users (paginated) |
| PATCH | /api/admin/users/:id/promote |
Promote user to admin |
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
MongoDB + Redis status |
- ✅ User registration & login with JWT access + refresh token rotation
- ✅ Password-protected short links (bcrypt)
- ✅ Custom aliases (4–20 chars)
- ✅ Link expiry with MongoDB TTL index
- ✅ Redis read-through caching (falls back to DB on error)
- ✅ BullMQ background queue for non-blocking click tracking
- ✅ Analytics: clicks by date & referrer with Recharts line chart
- ✅ QR code generation (PNG data URL)
- ✅ Admin panel: system stats, link moderation, user management
- ✅ Per-user + global rate limiting (Redis-backed, distributed)
- ✅ Graceful shutdown (closes DB, Redis, BullMQ workers)
- ✅ Health check endpoint
- ✅ Morgan HTTP logging
- ✅ Structured JSON error responses
- ✅ Automatic 401 token refresh in frontend (Axios interceptor)
Use MongoDB shell or Compass:
db.users.updateOne({ email: "you@example.com" }, { $set: { isAdmin: true } })Or promote via the Admin Panel once you have one admin.