A minimalistic, production-ready URL shortener application built with modern web technologies including React, Vite, ShadCN UI, Flask, and MariaDB.
- Clean and minimalistic user interface
- Fast URL shortening with MD5-based hash generation
- Click tracking and analytics
- One-to-one URL mapping (duplicate URLs return the same short code)
- Copy to clipboard functionality
- RESTful API design
- Containerized deployment with Docker
- Database persistence with MariaDB
Frontend:
- React 19 with TypeScript
- Vite (build tool)
- ShadCN UI component library
- Tailwind CSS for styling
- Lucide React for icons
Backend:
- Python 3.11
- Flask web framework
- Flask-CORS for cross-origin resource sharing
- MySQL Connector for database operations
- Python-dotenv for environment configuration
Database:
- MariaDB 12.0
- Indexed schema for optimized queries
- Docker Desktop (Windows, macOS, or Linux)
- 4GB RAM minimum
- 10GB free disk space
- Node.js v18 or higher
- Python 3.8 or higher
- MariaDB 10.5 or MySQL 8.0 or higher
- Git (for version control)
Docker provides the simplest and most reliable deployment method as it handles all dependencies automatically.
Ensure Docker Desktop is installed and running:
docker --version
docker-compose --versionIf Docker is not installed, download it from Docker Desktop.
Navigate to the project directory and run:
cd C:\smallurl
.\docker-start.ps1Alternatively, use docker-compose directly:
docker-compose up --buildTo run in detached mode (background):
docker-compose up -d --buildThe following services will be available:
- Frontend Application: http://localhost:5173
- Backend API: http://localhost:5000
- API Health Check: http://localhost:5000/health
- MariaDB Database: localhost:3306
To stop all services:
# If running in foreground, press Ctrl+C
# If running in background:
docker-compose down
# To stop and remove all data (including database):
docker-compose down -vFor local development without Docker, follow these steps:
Start your MariaDB/MySQL service and create the database:
# Connect to MariaDB
mysql -u root -p
# Create database and table
source backend/init_db.sqlOr manually execute:
CREATE DATABASE IF NOT EXISTS url_shortener;
USE url_shortener;
CREATE TABLE IF NOT EXISTS urls (
id INT AUTO_INCREMENT PRIMARY KEY,
original_url VARCHAR(2048) NOT NULL,
short_code VARCHAR(10) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
clicks INT DEFAULT 0,
INDEX idx_short_code (short_code),
INDEX idx_original_url (original_url(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Navigate to the backend directory:
cd backendCreate a Python virtual environment:
# Windows
python -m venv venv
venv\Scripts\activate
# macOS/Linux
python3 -m venv venv
source venv/bin/activateInstall dependencies:
pip install -r requirements.txtConfigure environment variables by editing the .env file:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_database_password
DB_NAME=url_shortenerStart the Flask backend:
python app.pyThe backend API will be available at http://localhost:5000
Open a new terminal and navigate to the frontend directory:
cd frontendInstall Node.js dependencies:
npm installStart the Vite development server:
npm run devThe frontend application will be available at http://localhost:5173
- Navigate to http://localhost:5173
- Enter a long URL in the input field (e.g.,
https://example.com/very/long/path) - Click the "Shorten URL" button
- Copy the generated short URL using the copy button
- Share the shortened URL
Visit the shortened URL (e.g., http://localhost:5000/abc123) and you will be automatically redirected to the original URL. Each access increments the click counter.
Access statistics for any shortened URL:
GET http://localhost:5000/stats/abc123Response:
{
"original_url": "https://example.com/very/long/path",
"short_code": "abc123",
"created_at": "2025-11-18T10:30:00",
"clicks": 42
}Creates a new shortened URL or returns existing short code for duplicate URLs.
Request:
{
"url": "https://example.com/very/long/url"
}Response (201 Created or 200 OK):
{
"short_code": "abc123"
}Error Response (400 Bad Request):
{
"error": "URL is required"
}Redirects to the original URL and increments the click counter.
Response: HTTP 302 redirect to original URL
Error Response (404 Not Found): "URL not found"
Retrieves statistics for a shortened URL.
Response (200 OK):
{
"original_url": "https://example.com/very/long/url",
"short_code": "abc123",
"created_at": "2025-11-18T10:30:00",
"clicks": 42
}Error Response (404 Not Found):
{
"error": "URL not found"
}Health check endpoint for monitoring.
Response (200 OK):
{
"status": "healthy",
"database": "connected"
}Error Response (500 Internal Server Error):
{
"status": "unhealthy",
"database": "disconnected"
}-
URL Shortening Process:
- User submits a URL via the frontend
- Frontend sends POST request to
/shortenendpoint - Backend validates and normalizes the URL
- Backend checks database for existing entry
- If URL exists, returns existing short code
- If new, generates MD5 hash (first 6 characters) as short code
- Handles hash collisions with retry mechanism
- Stores mapping in database
- Returns short code to frontend
-
URL Redirection Process:
- User accesses shortened URL
- Backend queries database for short code
- If found, increments click counter and redirects (HTTP 302)
- If not found, returns 404 error
-
Database Schema:
id: Auto-incrementing primary keyoriginal_url: Full URL (up to 2048 characters)short_code: 6-character unique identifier (indexed)created_at: Timestamp of creationclicks: Number of times accessed- Indexes on
short_codeandoriginal_urlfor query optimization
Error:
error during connect: Get "http://%2F%2F.%2Fpipe%2FdockerDesktopLinuxEngine/v1.51/...":
open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.
Solution:
- Open Docker Desktop from the Start Menu
- Wait for the Docker icon in the system tray to stop animating
- Verify Docker is running:
docker info - Run the application again
Error:
Bind for 0.0.0.0:5000 failed: port is already allocated
Solution:
Find and stop the process using the port:
# Find process using port
netstat -ano | findstr :5000
# Stop the process (replace PID with actual process ID)
taskkill /PID <PID> /FOr change the port in docker-compose.yml:
services:
backend:
ports:
- "5001:5000" # Change host port to 5001Error:
failed to solve with frontend dockerfile.v0
Solution:
Clear Docker cache and rebuild:
# Remove all containers and images
docker-compose down -v --rmi all
# Rebuild from scratch
docker-compose up --build --force-recreateError in backend logs:
Error connecting to MariaDB: Can't connect to MySQL server
Solution:
- Wait for database initialization (takes 10-15 seconds on first run)
- Check database health:
docker-compose ps - View database logs:
docker-compose logs database - Restart database container:
docker-compose restart database
Error:
'python' is not recognized as an internal or external command
Solution:
Ensure Python is installed and in PATH:
# Check Python installation
python --version
# Or try
python3 --version
# Add to PATH if needed (Windows)
# Control Panel > System > Advanced > Environment VariablesError:
mysql.connector.errors.DatabaseError: 2003: Can't connect to MySQL server
Solution:
-
Verify MariaDB is running:
# Windows Get-Service -Name MariaDB # Start if stopped Start-Service -Name MariaDB
-
Check credentials in
.envfile -
Verify database exists:
SHOW DATABASES;
Error in browser console:
Access to fetch at 'http://localhost:5000/shorten' from origin 'http://localhost:5173'
has been blocked by CORS policy
Solution:
-
Ensure Flask-CORS is installed:
pip install flask-cors
-
Verify CORS is enabled in
backend/app.py:from flask_cors import CORS app = Flask(__name__) CORS(app)
-
Restart the backend server
Error:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
Solution:
Clear npm cache and reinstall:
# Clear npm cache
npm cache clean --force
# Delete node_modules and package-lock.json
rm -rf node_modules package-lock.json
# Reinstall
npm install
# Or use legacy peer deps
npm install --legacy-peer-depsIssue: Previously generated short URLs return 404 after restarting Docker.
Cause: Database volume was removed with docker-compose down -v
Solution:
- Use
docker-compose down(without-vflag) to preserve data - To persist data permanently, the MariaDB volume is already configured in
docker-compose.yml
Solution:
-
Verify backend is running:
curl http://localhost:5000/health
-
Check backend logs:
docker-compose logs backend -
Ensure correct API URL in
frontend/src/App.tsx(should behttp://localhost:5000)
For production deployment, update the following:
-
Frontend API URL (
frontend/src/App.tsx):const response = await fetch('https://api.yourdomain.com/shorten', { // ... })
-
Backend Environment (
.env):DB_HOST=production-database-host DB_USER=production_user DB_PASSWORD=strong_password_here DB_NAME=url_shortener FLASK_ENV=production
-
Security Considerations:
- Use HTTPS for all connections
- Enable rate limiting on API endpoints
- Use strong database passwords
- Configure firewall rules
- Regular database backups
- Enable logging and monitoring
# Build frontend
cd frontend
npm run build
# The dist/ folder contains production-ready filessmallurl/
├── backend/
│ ├── app.py # Flask application
│ ├── requirements.txt # Python dependencies
│ ├── Dockerfile # Backend container config
│ ├── .env # Environment variables
│ └── init_db.sql # Database schema
├── frontend/
│ ├── src/
│ │ ├── App.tsx # Main React component
│ │ ├── components/ # UI components
│ │ └── lib/ # Utilities
│ ├── Dockerfile # Frontend container config
│ ├── package.json # Node dependencies
│ └── vite.config.ts # Vite configuration
├── docker-compose.yml # Multi-container orchestration
├── docker-start.ps1 # Quick start script
└── README.md # Documentation
# Backend tests (to be implemented)
cd backend
pytest
# Frontend tests (to be implemented)
cd frontend
npm test- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License. See the LICENSE file for details.
For issues, questions, or contributions, please open an issue on the GitHub repository.
- ShadCN UI for the component library
- Flask framework for the backend
- Vite for the blazing-fast development experience
- MariaDB for reliable data persistence