Skip to Content
Configuration Guide

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

VariableDescriptionDefaultExample
PORTServer port30013001
BASE_URLBase URL for generating short linkshttp://localhost:${PORT}https://short.example.com
NODE_ENVEnvironment modedevelopmentproduction

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=development

The 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 dev

Development 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.com

Building for Production

Compile TypeScript to JavaScript:

cd backend npm run build

This creates optimized JavaScript files in the dist/ directory.

Starting Production Server

npm start

Or with environment variables:

NODE_ENV=production PORT=8080 BASE_URL=https://short.example.com npm start

Production Considerations

  1. CORS Configuration: Restrict CORS to specific origins:

    // backend/src/index.ts app.use(cors({ origin: ['https://yourfrontend.com'], credentials: true }));
  2. Rate Limiting: Add rate limiting to prevent abuse:

    npm install express-rate-limit
    import 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);
  3. Request Logging: Use proper logging libraries:

    npm install winston
  4. 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 mongodb
import { 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 pg
import { 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 redis
import { 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:6379

Short 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-stopped

Run with Docker Compose:

docker-compose up -d

Monitoring 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