A simple, full-stack URL shortener built with:
- BackEnd: Express + TypeScript + Prisma (Postgres)
- FrontEnd: Next.js (React) UI
- DB: PostgreSQL (Postgres 15)
- Auth: JSON Web Tokens (JWT)
- URL Shortener — Full Stack 🚀
- Table of Contents
- Overview
- Features ✅
- Architecture 🔧
- Prerequisites
- Environment variables
- Quickstart
- Run with Docker Compose (full stack) [Recommended]
- Environment variables
- Extra Cautions: Scripts to help you to deep drive .
- Backend (Prisma & scripts)
- Frontend
- API endpoints (summary)
- Authentication flow
- Troubleshooting
⚠️
This repo contains a lightweight URL shortener service with a web dashboard for authenticated users. Users can create shortened URLs, list/manage their URLs, and the server handles redirects for short links.
- User authentication (signup / signin) using JWT
- Create, list, update, delete short URLs per user
- PostgreSQL database with Prisma ORM
- Dev-friendly setup (Docker + local scripts)
- Frontend: Next.js app (client-side pages and components) — runs on port 3001 in dev
- Backend: Express + TypeScript, exposes REST API under
/api/*— runs on port 3000 in dev - Database: Postgres, managed by Prisma
- Node.js 20+ and npm
- Docker & Docker Compose (if using containers)
- Git
Examples are provided at BackEnd/.env.example and FrontEnd/.env.local.example.
BackEnd (BackEnd/.env):
DATABASE_URL="postgresql://user:password@postgres_db:5432/url_shortener"
JWT_SECRET="your_jwt_secret"
PORT=3000
NODE_ENV=developmentFrontEnd (FrontEnd/.env.local):
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000Tip: When running with Docker Compose, use the docker service names in
DATABASE_URL(e.g.,postgres_db) so containers can reach the DB.
Clone the repository:
git clone <repo-url> url-shortener
cd url-shortenerStart only the database (recommended when developing locally):
docker compose up -d postgres_dbThen set up and run the backend:
cd BackEnd
npm install
# create .env (see Environment variables section)
npx prisma generate
npx prisma migrate dev
npm run devStart the frontend:
cd ../FrontEnd
npm install
npm run dev -- -p 3001Open the app at: http://localhost:3001
Note: Backend default is
http://localhost:3000. The frontend usesNEXT_PUBLIC_API_BASE_URLto configure the API base URL.
The provided docker-compose.yaml includes services for nextjs, postgres_db, and express_app. To run the full stack locally with Docker Compose:
docker compose up --buildAfter build the container build the table in Database first using below command.
docker compose exec express_app npx prisma migrate deployContainers:
nextjs: Next.js app (maps host 3001 -> container 3000)express_app: Backend service (maps host 3000 -> container 3000)postgres_db: PostgreSQL database (maps host 5432 -> container 5432)
If you only want the DB:
docker compose up -d postgres_dbExamples are provided at BackEnd/.env.example and FrontEnd/.env.local.example.
BackEnd (BackEnd/.env):
DATABASE_URL="postgresql://user:password@postgres_db:5432/url_shortener"
JWT_SECRET="your_jwt_secret"
PORT=3000
NODE_ENV=developmentFrontEnd (FrontEnd/.env.local):
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000Tip: When running with Docker Compose, use the docker service names in
DATABASE_URL(e.g.,postgres_db) so containers can reach the DB.
Key scripts (in BackEnd/package.json):
npm run dev— start backend in dev mode with auto-reloadnpm run build— compile TypeScriptnpm start— run the compiled build
Prisma:
npx prisma generate # generate client
npx prisma migrate dev # apply migrations (local dev)
npx prisma db push # push schema without migrations (dev only)Migrations live under BackEnd/prisma/migrations/.
Key scripts (in FrontEnd/package.json):
npm run dev— start Next.js dev server (use-p 3001to run on 3001)npm run buildandnpm start— build and serve
The frontend stores the JWT token in a cookie (token) after signing in.
Auth:
POST /api/auth/signup— register (body:{ email, password })POST /api/auth/signin— login (body:{ email, password }) → returns{ token }GET /api/auth/me— (protected) returns user info (id, email)
URLs (protected):
POST /api/urls/url-create— create short URL (body:{ originalURL })GET /api/urls/all— list user URLsGET /api/urls/:id— get URL detailPUT /api/urls/:id— update URLDELETE /api/urls/delete/:id— delete URL
Redirects (public):
/r/:shortId(or similar) — redirects to the original URL (checkBackEndroutes)
- User signs in via
/api/auth/signinand receives a JWT. - Frontend stores the token (cookie) and includes it in the
Authorization: Bearer <token>header for protected API requests. - Backend middleware validates the token on protected routes and attaches
userIdto the request.
- CORS: Ensure
http://localhost:3001(or your frontend origin) is allowed in backend CORS settings. - Database connection: Verify
DATABASE_URLpoints to a running Postgres instance and migrations applied. - Token errors: If you receive 401s, remove the stored cookie and re-login.
- Ports: If 3000/3001/5432 already in use, change host ports in
docker-compose.yamlor use different dev ports.