Source: Jolli-sample-repos/url-shortener Last Updated: 4/8/2026
Configuration Guide
Learn how to configure the URL Shortener for different environments and use cases.
Environment Variables
The backend application can be configured using environment variables. These can be set in a .env file in the backend directory or passed directly when running the application.
Available Variables
| Variable | Description | Default | Example |
|---|---|---|---|
PORT | Server port | 3001 | 3001 |
BASE_URL | Base URL for generating short links | http://localhost:${PORT} | https://short.example.com |
NODE_ENV | Environment mode | development | production |
Creating a .env File
Create a .env file in the backend directory:
# Server Configuration
PORT=3001
# Base URL (used in responses)
BASE_URL=http://localhost:3001
# Environment
NODE_ENV=developmentThe application will automatically load these variables using Node.js’s native environment variable support.
Frontend Configuration
The frontend configuration is located in frontend/js/config.js (if it exists) or directly in the JavaScript files.
API Endpoint Configuration
To point the frontend to your backend API, update the API base URL:
// In frontend/js/app.js or similar
const API_BASE_URL = 'http://localhost:3001';For production:
const API_BASE_URL = 'https://api.example.com';Development Configuration
Backend Development Mode
The development mode includes:
- Auto-reload on file changes (via nodemon)
- Detailed error messages
- Request logging to console
Start development mode:
cd backend
npm run devDevelopment Settings
In backend/src/index.ts, the following middleware is configured:
// CORS - allows requests from any origin in development
app.use(cors());
// Request logging
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});Production Configuration
Environment Setup
For production deployment, set these environment variables:
# Production environment
NODE_ENV=production
# Production port (often set by hosting provider)
PORT=8080
# Your production domain
BASE_URL=https://short.example.comBuilding for Production
Compile TypeScript to JavaScript:
cd backend
npm run buildThis creates optimized JavaScript files in the dist/ directory.
Starting Production Server
npm startOr with environment variables:
NODE_ENV=production PORT=8080 BASE_URL=https://short.example.com npm startProduction Considerations
-
CORS Configuration: Restrict CORS to specific origins:
// backend/src/index.ts app.use(cors({ origin: ['https://yourfrontend.com'], credentials: true })); -
Rate Limiting: Add rate limiting to prevent abuse:
npm install express-rate-limitimport rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs }); app.use('/api/', limiter); -
Request Logging: Use proper logging libraries:
npm install winston -
Process Management: Use PM2 or similar:
npm install -g pm2 pm2 start dist/index.js --name url-shortener
Storage Configuration
The current implementation uses in-memory storage. For production, you’ll want to integrate a database.
Current Implementation
Located in backend/src/storage.ts:
class UrlStorage {
private urls: Map<string, ShortUrl> = new Map();
private longUrlIndex: Map<string, string> = new Map();
// Storage methods...
}Database Integration
To add database support, you can extend the storage layer:
Option 1: MongoDB
npm install mongodbimport { MongoClient } from 'mongodb';
class MongoUrlStorage implements IUrlStorage {
private collection;
constructor(connectionString: string) {
const client = new MongoClient(connectionString);
this.collection = client.db('urlshortener').collection('urls');
}
async create(url: ShortUrl): Promise<ShortUrl> {
await this.collection.insertOne(url);
return url;
}
// Implement other methods...
}Option 2: PostgreSQL
npm install pgimport { Pool } from 'pg';
class PostgresUrlStorage implements IUrlStorage {
private pool: Pool;
constructor(connectionConfig) {
this.pool = new Pool(connectionConfig);
}
async create(url: ShortUrl): Promise<ShortUrl> {
await this.pool.query(
'INSERT INTO urls (short_code, long_url, created_at, clicks) VALUES ($1, $2, $3, $4)',
[url.shortCode, url.longUrl, url.createdAt, url.clicks]
);
return url;
}
// Implement other methods...
}Option 3: Redis
npm install redisimport { createClient } from 'redis';
class RedisUrlStorage implements IUrlStorage {
private client;
constructor(redisUrl: string) {
this.client = createClient({ url: redisUrl });
this.client.connect();
}
async create(url: ShortUrl): Promise<ShortUrl> {
await this.client.set(
`url:${url.shortCode}`,
JSON.stringify(url)
);
return url;
}
// Implement other methods...
}Storage Environment Variables
Add these for database configuration:
# Database Configuration
DATABASE_TYPE=mongodb # or 'postgres', 'redis', 'memory'
DATABASE_URL=mongodb://localhost:27017/urlshortener
# Or for PostgreSQL
DATABASE_URL=postgresql://user:password@localhost:5432/urlshortener
# Or for Redis
REDIS_URL=redis://localhost:6379Short Code Generation Configuration
The short code generation is configured in backend/src/generator.ts:
// Characters for short codes (alphanumeric)
const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const DEFAULT_LENGTH = 6;Customizing Short Codes
You can modify these constants to change the short code behavior:
// Longer codes (more unique combinations)
const DEFAULT_LENGTH = 8;
// URL-safe characters only
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
// Numbers only
const ALPHABET = '0123456789';Collision Probability: With 6-character alphanumeric codes, you have 62^6 = 56.8 billion possible combinations.
Validation Configuration
Request validation is handled by Zod schemas in backend/src/validator.ts:
Custom Code Validation
export const createUrlSchema = z.object({
longUrl: z.string().url({ message: 'Must be a valid URL' }),
customCode: z
.string()
.regex(/^[a-zA-Z0-9]+$/, 'Custom code must be alphanumeric')
.min(4, 'Custom code must be at least 4 characters')
.max(20, 'Custom code must be at most 20 characters')
.optional(),
expiresAt: z.string().datetime({ message: 'Must be a valid ISO 8601 datetime' }).optional(),
});You can modify these rules:
// Allow hyphens and underscores in custom codes
customCode: z
.string()
.regex(/^[a-zA-Z0-9_-]+$/, 'Custom code must be alphanumeric with hyphens or underscores')
.min(3, 'Custom code must be at least 3 characters')
.max(50, 'Custom code must be at most 50 characters')
.optional(),API Documentation Configuration
Swagger documentation is configured in backend/src/swagger.ts:
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'URL Shortener API',
version: '1.0.0',
description: 'A simple URL shortening service with analytics',
},
servers: [
{
url: 'http://localhost:3001',
},
],
},
apis: ['./src/routes.ts'],
};Update the server URL for your environment:
servers: [
{
url: process.env.BASE_URL || 'http://localhost:3001',
description: 'API Server',
},
],Docker Configuration
Create a Dockerfile in the backend directory:
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Build TypeScript
RUN npm run build
# Expose port
EXPOSE 3001
# Set environment to production
ENV NODE_ENV=production
# Start server
CMD ["npm", "start"]Create a docker-compose.yml in the root directory:
version: '3.8'
services:
backend:
build: ./backend
ports:
- "3001:3001"
environment:
- NODE_ENV=production
- PORT=3001
- BASE_URL=http://localhost:3001
restart: unless-stopped
frontend:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./frontend:/usr/share/nginx/html:ro
restart: unless-stoppedRun with Docker Compose:
docker-compose up -dMonitoring and Logging
Health Check Endpoint
The service includes a health check at backend/src/index.ts:74:
app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});Use this for monitoring and load balancer health checks.
Logging Configuration
For production logging, consider using Winston:
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
}Next Steps
- Development Setup - Set up your development environment
- Deployment Guide - Deploy to production
- Architecture Overview - Understand the system design