UNPKG

@developers-joyride/shortify

Version:

High performance URL shortener library with multi-database support (MongoDB, SQLite, PostgreSQL, MySQL)

339 lines (255 loc) 11.1 kB
# Shortify A high-performance URL shortener library for Node.js, written in TypeScript with support for multiple databases (MongoDB, SQLite, PostgreSQL, MySQL). ## Features - ⚡️ **High performance**: Optimized for high throughput - 🔒 **Type safety**: Full TypeScript support - 📈 **Analytics**: Track clicks and usage stats for your shortened URLs - ⌛️ **Expiration support**: Set expiration dates for your shortened URLs - 🛠️ **Customizable**: Use custom URL IDs or auto-generated ones - 🔄 **Automatic retry**: Built-in database connection retry with exponential backoff - 🧠 **Unique URL IDs**: Each shorten() call generates a unique URL ID, ensuring no conflicts - 🗄️ **Multi-database support**: Choose between MongoDB, SQLite, PostgreSQL, or MySQL - 🔧 **Database abstraction**: Easy to switch between databases without changing your code ## Installation ```bash # Using npm npm i @developers-joyride/shortify # Using yarn yarn add @developers-joyride/shortify ``` ## Usage ### Basic Usage #### MongoDB (Original way - Backward Compatible) ```typescript import Shortify from "@developers-joyride/shortify"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); // Create a new Shortify instance with MongoDB (backward compatible) const shortify = Shortify.createWithMongo( "https://your-domain.com/", // Base URL for your shortened links process.env.MONGODB_URI || "mongodb://localhost:27017/shortify" // MongoDB connection URI ); // Connect to database await shortify.connect(); // Shorten a URL const result = await shortify.shorten( "https://example.com/very-long-url-that-needs-shortening" ); console.log(`Shortened URL: ${result.shortUrl}`); // https://your-domain.com/abc123 // Resolve a shortened URL const originalUrl = await shortify.resolve("abc123"); console.log(`Original URL: ${originalUrl}`); // https://example.com/very-long-url-that-needs-shortening // Get statistics for a shortened URL const stats = await shortify.getStats("abc123"); console.log(`Clicks: ${stats?.clicks}`); // Delete a shortened URL await shortify.delete("abc123"); // Disconnect when done await shortify.disconnect(); ``` #### SQLite ```typescript import Shortify, { DatabaseConfig } from "@developers-joyride/shortify"; const dbConfig: DatabaseConfig = { type: "sqlite", database: "./shortify.db", // Path to SQLite database file tableName: "my_custom_urls", // Optional: Custom table name (default: "urls") maxRetries: 3, retryDelay: 2000, }; const shortify = new Shortify("https://your-domain.com/", dbConfig); await shortify.connect(); // Use the same API as before const result = await shortify.shorten("https://example.com/long-url"); console.log(`Shortened URL: ${result.shortUrl}`); ``` #### MongoDB (New Format) ```typescript import Shortify, { DatabaseConfig } from "@developers-joyride/shortify"; const dbConfig: DatabaseConfig = { type: "mongodb", connectionString: "mongodb://localhost:27017/shortify", collectionName: "my_custom_urls", // Optional: Custom collection name (default: "urls") maxRetries: 3, retryDelay: 2000, }; const shortify = new Shortify("https://your-domain.com/", dbConfig); await shortify.connect(); // Use the same API as before const result = await shortify.shorten("https://example.com/long-url"); console.log(`Shortened URL: ${result.shortUrl}`); ``` **Custom Collection/Table Names**: You can specify custom collection names for MongoDB using the `collectionName` option, or custom table names for SQLite, PostgreSQL, and MySQL using the `tableName` option. This is useful when you want to organize your data differently or avoid conflicts with existing collections/tables. If not specified, they default to `"urls"`. > **Note**: Collection and table names are now preserved exactly as specified, including case sensitivity. Previously, MongoDB collection names were automatically converted to lowercase, and SQL databases would convert unquoted table names to lowercase on case-insensitive filesystems. #### MySQL ```typescript import Shortify, { DatabaseConfig } from "@developers-joyride/shortify"; const dbConfig: DatabaseConfig = { type: "mysql", host: "localhost", port: 3306, database: "shortify", username: "root", password: "your_password", tableName: "my_custom_urls", // Optional: Custom table name (default: "urls") maxRetries: 3, retryDelay: 2000, }; const shortify = new Shortify("https://your-domain.com/", dbConfig); await shortify.connect(); // Use the same API as before const result = await shortify.shorten("https://example.com/long-url"); console.log(`Shortened URL: ${result.shortUrl}`); ``` #### PostgreSQL ```typescript import Shortify, { DatabaseConfig } from "@developers-joyride/shortify"; const dbConfig: DatabaseConfig = { type: "postgresql", host: "localhost", port: 5432, database: "shortify", username: "postgres", password: "your_password", tableName: "my_custom_urls", // Optional: Custom table name (default: "urls") maxRetries: 3, retryDelay: 2000, }; const shortify = new Shortify("https://your-domain.com/", dbConfig); await shortify.connect(); // Use the same API as before const result = await shortify.shorten("https://example.com/long-url"); console.log(`Shortened URL: ${result.shortUrl}`); ``` ### Advanced Options ```typescript // Shorten a URL with custom options const result = await shortify.shorten("https://example.com", { customUrlId: "custom-id", // Use a custom ID instead of auto-generated one urlLength: 6, // Set length of auto-generated ID (ignored if customUrlId is set) expiresInDays: 30, // URL will expire after 30 days baseUrl: "https://short.ly/", // Override the base URL for this specific link }); // Each shorten() call generates a unique URL ID, even for the same original URL const result1 = await shortify.shorten("https://example.com"); const result2 = await shortify.shorten("https://example.com"); console.log(result1.urlId !== result2.urlId); // true - different URL IDs console.log( (await shortify.resolve(result1.urlId)) === (await shortify.resolve(result2.urlId)) ); // true - same original URL // Create with connection options (MongoDB backward compatibility) const shortify = Shortify.createWithMongo( "https://your-domain.com/", "mongodb://localhost:27017/shortify", { maxRetries: 5, // Maximum number of connection retry attempts retryDelay: 5000, // Initial delay between retries in ms (doubles with each retry) collectionName: "my_custom_urls", // Optional: Custom collection name (default: "urls") } ); ``` ## API Reference ### `Shortify` Class #### Constructor ```typescript constructor(baseUrl: string, dbConfig: DatabaseConfig) ``` #### Static Methods - `createWithMongo(baseUrl: string, mongoUri: string, options?: { maxRetries?: number, retryDelay?: number })`: Create instance with MongoDB (backward compatibility) #### Instance Methods - `connect()`: Connect to database - `disconnect()`: Disconnect from database - `isReady()`: Check if the connection is established - `shorten(url: string, options?: ShortifyOptions)`: Shorten a URL - `resolve(urlId: string)`: Resolve a short URL to its original URL - `getStats(urlId: string)`: Get statistics for a shortened URL - `delete(urlId: string)`: Delete a shortened URL ### `DatabaseConfig` Interface ```typescript interface DatabaseConfig { type: "mongodb" | "sqlite" | "postgresql" | "mysql"; connectionString?: string; // For MongoDB database?: string; // For SQLite (file path) or PostgreSQL/MySQL (database name) host?: string; // For PostgreSQL/MySQL port?: number; // For PostgreSQL/MySQL username?: string; // For PostgreSQL/MySQL password?: string; // For PostgreSQL/MySQL maxRetries?: number; // Maximum connection retry attempts retryDelay?: number; // Initial delay between retries in ms collectionName?: string; // For MongoDB: Custom collection name (default: "urls") tableName?: string; // For SQLite/PostgreSQL/MySQL: Custom table name (default: "urls") } ``` ### `ShortifyOptions` Interface ```typescript interface ShortifyOptions { baseUrl?: string; // Override the base URL urlLength?: number; // Length of the generated URL ID customUrlId?: string; // Custom URL ID expiresInDays?: number; // Number of days until the URL expires } ``` ## Database Support Shortify supports multiple database types, allowing you to choose the best option for your use case: ### MongoDB - **Best for**: High-traffic applications, complex queries, horizontal scaling - **Setup**: Requires MongoDB server running - **Configuration**: Use `connectionString` parameter ### SQLite - **Best for**: Simple applications, development, embedded systems - **Setup**: No server required, file-based - **Configuration**: Use `database` parameter (file path) ### MySQL - **Best for**: Web applications, compatibility with existing MySQL infrastructure - **Setup**: Requires MySQL server running - **Configuration**: Use `host`, `port`, `database`, `username`, `password` parameters ### PostgreSQL - **Best for**: ACID compliance, complex relationships, enterprise applications - **Setup**: Requires PostgreSQL server running - **Configuration**: Use `host`, `port`, `database`, `username`, `password` parameters ## Examples Check out the `examples/` directory for comprehensive usage examples: - `basic-usage.ts` - Basic URL shortening functionality - `sqlite-example.ts` - SQLite database usage - `postgresql-example.ts` - PostgreSQL database usage - `multi-database-usage.ts` - Multi-database configuration examples - `click-tracking-example.ts` - **Click tracking and analytics demonstration** - `express-server.ts` - Express.js server integration - `custom-collection-example.ts` - **Custom collection name demonstration** - `custom-table-names-example.ts` - **Custom table names for all database types** ### Click Tracking & Analytics The library automatically tracks clicks when URLs are resolved. Here's a quick example: ```typescript // Create a shortened URL const result = await shortify.shorten("https://example.com/my-page"); // Resolve the URL (this increments the click count) const originalUrl = await shortify.resolve(result.urlId); // Get statistics const stats = await shortify.getStats(result.urlId); console.log(`Clicks: ${stats?.clicks}`); // Will show 1 console.log(`Created: ${stats?.createdAt}`); console.log(`Expires: ${stats?.expiresAt || "Never"}`); ``` For a comprehensive demonstration of click tracking features, run: ```bash npx ts-node examples/click-tracking-example.ts ``` ## Migration Guide ### From Previous Version If you're upgrading from a previous version that only supported MongoDB, your code will continue to work without changes: ```typescript // Old way (still works) const shortify = new Shortify( "https://your-domain.com/", "mongodb://localhost:27017/shortify" ); // New way (recommended) const shortify = Shortify.createWithMongo( "https://your-domain.com/", "mongodb://localhost:27017/shortify" ); ``` ## License MIT