A professional URL shortener with UTM parameter management, built with a modern stack using PostgreSQL, Prisma, and Docker for easy deployment and development.
- π URL Shortening - Create short, memorable links with custom or auto-generated slugs
- π UTM Management - Add, manage, and track UTM parameters with predefined options
- π Multi-Domain Support - Support for multiple custom domains for redirection
- π― Click Tracking - Track clicks and analytics for each short link
- ποΈ Modern Database - PostgreSQL with Prisma ORM for type safety and migrations
- π¨ Modern UI - React + Tailwind CSS with responsive design
- π³ Docker Ready - Complete Docker setup for easy development and deployment
- π Flexible Deployment - Deploy to any platform that supports Docker
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β React Frontend β β Unified Server β β PostgreSQL DB β
β (Built into βββββΆβ Express API βββββΆβ + pgAdmin β
β Express) β β + Redirect β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
- Node.js 18+ and npm
- Docker and Docker Compose
- Git
-
Clone and Setup
git clone <repository-url> cd linkpipe
-
Start with Docker Compose β Recommended
# Copy environment template cp .env.example .env # Start all services docker-compose up -d
This will start:
- Unified Server: http://localhost:8000 (API + Frontend + Redirect)
- PostgreSQL: localhost:5433
- pgAdmin: http://localhost:8003
-
Initialize Database
# Run Prisma migrations cd backend npx prisma migrate dev # Seed with sample data npm run db:seed
-
Access the Application
- Frontend: http://localhost:8000
- API Health: http://localhost:8000/health
- API Endpoints: http://localhost:8000/api
- pgAdmin: http://localhost:8003 (admin@linkpipe.local / admin)
linkpipe/
βββ frontend/ # React + Vite frontend (built into backend/public/)
β βββ src/
β β βββ components/ # Reusable UI components
β β βββ pages/ # Route pages
β β βββ lib/ # API client and utilities
β β βββ App.tsx # Main app component
β βββ vercel.json # Vercel deployment config
βββ backend/ # Unified Node.js server (API + Frontend + Redirect)
β βββ src/
β β βββ routes/ # Express routes (API + Redirect)
β β βββ lib/ # Prisma database layer
β β βββ middleware/ # Express middleware
β β βββ types.ts # TypeScript types
β βββ prisma/ # Prisma schema and migrations
β β βββ schema.prisma # Database schema
β β βββ migrations/ # Database migrations
β βββ public/ # Built React frontend (served by Express)
β βββ sql/ # SQL initialization scripts
βββ infra/ # Infrastructure as Code (Pulumi)
β βββ index.ts # Main infrastructure definition
β βββ package.json # Pulumi dependencies
β βββ deploy.sh # Deployment automation script
β βββ README.md # Infrastructure documentation
βββ shared/ # Shared TypeScript types and utilities
βββ docker-compose.yml # Unified Docker setup
LinkPipe uses a single .env file in the root directory for all services:
# Local Development Ports
LINKPIPE_PORT=8000
POSTGRES_PORT=5433
PGADMIN_PORT=8003
# Database Configuration (PostgreSQL + Prisma)
DATABASE_URL=postgresql://linkpipe:linkpipe@localhost:5433/linkpipe
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=linkpipe
POSTGRES_USER=linkpipe
POSTGRES_PASSWORD=linkpipe
# Local Development Configuration
NODE_ENV=development
SERVE_STATIC=true
# Authentication Configuration
JWT_SECRET=your-super-secret-jwt-key-change-in-production
SESSION_SECRET=your-super-secret-session-key-change-in-productionAll ports are configurable via environment variables. If you have conflicts:
# Edit .env file
LINKPIPE_PORT=9000
POSTGRES_PORT=5434
PGADMIN_PORT=9003Deploy to AWS using Infrastructure as Code with Pulumi:
# Navigate to infrastructure directory
cd infra
# Run the deployment script (offers to generate secrets automatically)
./deploy.sh dev latest mattstratton/linkpipe
# Or deploy manually
npm install
pulumi stack init dev
pulumi config set aws:region us-east-1
pulumi config set --secret linkpipe:dbPassword your-secure-password
pulumi config set --secret linkpipe:jwtSecret your-jwt-secret
pulumi config set --secret linkpipe:sessionSecret your-session-secret
pulumi upπ‘ Pro Tip: The deployment script can automatically generate secure secrets for you, or you can provide your own.
π Secret Requirements:
- Database Password: Minimum 8 characters
- JWT Secret: Minimum 32 characters (recommended: 64+)
- Session Secret: Minimum 32 characters (recommended: 64+)
LinkPipe supports multiple domains with automatic HTTPS. See docs/MULTI_DOMAIN_SETUP.md for detailed instructions.
Key Features:
- External DNS Support: Works with any DNS provider (GoDaddy, Namecheap, Cloudflare, etc.)
- Automatic HTTPS: SSL certificates managed by AWS Certificate Manager
- Multiple Domains: Single certificate covers all configured domains
- HTTP to HTTPS Redirect: Automatic redirect for security
Quick Setup:
# Deploy with your primary domain
pulumi config set primaryDomain "linkpipe.example.com"
# Add additional domains
pulumi config set --path additionalDomains '["link2.example.com", "link3.example.com"]'
# Deploy changes
pulumi up --yesDNS Configuration: After deployment, Pulumi will output the required DNS records:
- Certificate Validation: TXT records for SSL certificate validation
- Traffic Routing: CNAME records pointing to the ALB
SSL Certificate:
- SSL certificate is automatically provisioned via AWS Certificate Manager
- HTTPS redirect is automatically configured
- Certificate renewal is handled automatically
Features:
- ποΈ ECS Fargate - Serverless container orchestration
- ποΈ RDS PostgreSQL - Managed database with high availability
- π§ Application Database Setup - Tables and initial data created by application startup
- π Application Load Balancer - HTTP/HTTPS traffic distribution
- π VPC & Security Groups - Isolated network infrastructure
- π CloudWatch - Logging and monitoring
- π Auto-scaling - Automatic scaling based on demand
- π‘οΈ SSL/TLS - Automatic HTTPS with ACM certificates
Cost: ~$55-75/month for production setup
# Build and run production containers
docker-compose -f docker-compose.prod.yml up -d --build-
Deploy Backend to Railway
cd backend # Connect to Railway and deploy
-
Deploy Frontend to Vercel
cd frontend # Update VITE_API_URL in vercel.json vercel --prod
# Clone and setup on your VPS
git clone <repository-url>
cd linkpipe
cp .env.example .env
# Edit .env with production values
docker-compose up -dThe application uses Prisma with PostgreSQL:
-- Links table
CREATE TABLE links (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug VARCHAR(50) UNIQUE NOT NULL,
url TEXT NOT NULL,
domain VARCHAR(255) DEFAULT 'localhost:8000',
utm_source VARCHAR(255),
utm_medium VARCHAR(255),
utm_campaign VARCHAR(255),
utm_term VARCHAR(255),
utm_content VARCHAR(255),
description TEXT,
tags TEXT[],
is_active BOOLEAN DEFAULT true,
click_count INTEGER DEFAULT 0,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Settings table (stores domains, UTM options, etc.)
CREATE TABLE settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key VARCHAR(255) UNIQUE NOT NULL,
value JSONB NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);Important: Custom domains are managed in the database settings table, not environment variables.
# Get available domains
curl http://localhost:8000/api/settings/domains
# Update domains
curl -X PUT http://localhost:8000/api/settings/domains \
-H "Content-Type: application/json" \
-d '{
"value": ["localhost:8000", "short.example.com"],
"description": "Available domains for short links"
}'The application comes with default domains seeded in the database:
localhost:8000(development)short.example.com(example production domain)
# Generate Prisma client
cd backend
npx prisma generate
# Run migrations
npx prisma migrate dev
# Reset database
npx prisma migrate reset
# Seed database
npm run db:seed
# Open Prisma Studio
npx prisma studio- Update Database Schema in
backend/prisma/schema.prisma - Create Migration with
npx prisma migrate dev - Update API in
backend/src/routes/ - Update Frontend in
frontend/src/ - Update Types in
shared/src/types.ts
# Start development environment
docker-compose up -d
# View logs
docker-compose logs -f
# Rebuild services
docker-compose up -d --build
# Stop all services
docker-compose down
# Clean everything
docker-compose down -v# Run backend tests
cd backend && npm test
# Run frontend tests
cd frontend && npm test
# Run all tests
npm test# Lint all packages
npm run lint
# Fix linting issues
npm run lint:fixPOST /api/links # Create short link
GET /api/links # List all links
GET /api/links/:slug # Get specific link
PUT /api/links/:slug # Update link
DELETE /api/links/:slug # Delete link
HEAD /api/links/:slug # Check if slug exists
GET /api/settings # Get all settings
GET /api/settings/:key # Get specific setting
PUT /api/settings/:key # Update setting
PUT /api/settings # Update multiple settings
GET /:slug # Redirect to target URL (increment click count)
// Create a short link
const response = await fetch('/api/links', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com',
slug: 'my-link', // Optional
domain: 'short.example.com', // Optional
utm_params: {
utm_source: 'newsletter',
utm_medium: 'email'
},
tags: ['marketing'],
description: 'Marketing campaign link'
})
});
// Get all links
const links = await fetch('/api/links').then(r => r.json());
// Redirect (increments click count)
window.location.href = 'http://localhost:8000/example';| Component | Service | Est. Monthly Cost |
|---|---|---|
| Frontend | Vercel/Netlify | $0-20 |
| Backend | Railway/Render/Railway | $5-20 |
| Database | PostgreSQL (Railway/PlanetScale) | $5-20 |
| Domain & DNS | Cloudflare/Route 53 | $0-15 |
Total: $10-75/month depending on usage and platform
-
Port Conflicts
# Check what's using the ports lsof -i :3000 lsof -i :8000 lsof -i :8001 # Change ports in .env file FRONTEND_PORT=4000 BACKEND_PORT=9000 REDIRECT_PORT=9001
-
Database Connection Issues
# Check PostgreSQL is running docker-compose ps postgres # Restart PostgreSQL docker-compose restart postgres # Check logs docker-compose logs postgres
-
Prisma Issues
# Regenerate Prisma client cd backend npx prisma generate # Reset database npx prisma migrate reset # Check database connection npx prisma db push --preview-feature
-
Build Issues
# Clean and rebuild docker-compose down docker-compose up -d --build # Check logs docker-compose logs -f
# Enable debug logging
DEBUG=linkpipe:* docker-compose up
# View specific service logs
docker-compose logs frontend
docker-compose logs backend
docker-compose logs postgres- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature - Commit changes:
git commit -am 'Add new feature' - Push to the branch:
git push origin feature/new-feature - Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with React, Node.js, PostgreSQL, and Prisma
- UI components inspired by shadcn/ui
- Icons by Lucide React
- Database management with Prisma ORM
- Containerized with Docker
LinkPipe - Making URL management simple and powerful! π
# Quick start - just run this!
git clone <repository-url>
cd linkpipe
cp .env.example .env
docker-compose up -d
# Your services will be available at:
# Unified App: http://localhost:8000 (Frontend + API + Redirect)
# pgAdmin: http://localhost:8003