UNPKG

@unitio-code/url-shortener

Version:
286 lines (201 loc) 8.84 kB
# URL Shortener A simple and lightweight URL shortening library for Node.js. This package provides functionality to create short URLs from long ones and decode them back, with support for custom storage backends (like Redis) and expiration (TTL). ## Table of Contents - [Installation](#installation) - [Usage](#usage) - [Basic Usage](#basic-usage) - [Creating a Short URL](#creating-a-short-url) - [Customization Options](#customization-options) - [Using Different Hash Algorithms](#using-different-hash-algorithms) - [Decoding a Short URL](#decoding-a-short-url) - [Advanced Usage](#advanced-usage) - [Customizing URL Structure](#customizing-url-structure) - [Using Redis Storage](#using-redis-storage) - [URL Expiration (TTL)](#url-expiration-ttl) - [Collision Handling](#collision-handling) - [How It Works](#how-it-works) - [API Reference](#api-reference) - [License](#license) ## Installation ```bash npm install @unitio-code/url-shortener ``` If you plan to use Redis storage, you should also install `ioredis`: ```bash npm install ioredis ``` ## Usage ### Creating a Short URL The library is now fully asynchronous. You should use `await` or `.then()` when calling `createShortUrl`. #### 1. Create a short URL with required domain parameter ```typescript import { createShortUrl } from "@unitio-code/url-shortener"; async function example() { const shortUrl = await createShortUrl( "https://example.com/very/long/path/with/many/parameters?param1=value1&param2=value2", "short.url" ); console.log(shortUrl); // Output: short.url/r/Ab3x7Z (example) } ``` #### 2. Create a short URL with custom domain ```typescript import { createShortUrl } from "@unitio-code/url-shortener"; const customShortUrl = await createShortUrl( "https://example.com/very/long/path", "myshort.link" ); console.log(customShortUrl); // Output: myshort.link/r/Xy4p9Q (example) ``` #### 3. Create a short URL with customized options ```typescript import { createShortUrl } from "@unitio-code/url-shortener"; const customizedUrl = await createShortUrl( "https://example.com/very/long/path", "myshort.link", { includeProtocol: true, protocol: "https", includeRedirectPath: false, } ); console.log(customizedUrl); // Output: https://myshort.link/Xy4p9Q (example) ``` ### Customization Options The `createShortUrl` function accepts a wide range of options for customization: ```typescript interface CreateShortUrlOptions { // Domain options includeProtocol?: boolean; // Whether to include protocol (default: false) protocol?: string; // Protocol to use (default: 'https') // Path options includeRedirectPath?: boolean; // Whether to include the redirect path segment (default: true) redirectPathSegment?: string; // Custom path segment (default: 'r') pathSeparator?: string; // Custom separator between path segments (default: '/') // Hash algorithm options hashAlgorithm?: "djb2" | "sdbm" | "custom"; // Hash algorithm to use (default: 'djb2') customHashFn?: (url: string) => number; // Custom hash function // Storage and Persistence storage?: StorageInterface; // Custom storage implementation (default: MemoryStorage) ttl?: number; // Time to live in seconds maxRetries?: number; // Maximum retries for collision handling (default: 10) } ``` ### Using Different Hash Algorithms #### Using the SDBM hash algorithm ```typescript const sdbmUrl = await createShortUrl("https://example.com/path", "short.url", { hashAlgorithm: "sdbm", }); ``` #### Using a custom hash function ```typescript const customHashUrl = await createShortUrl("https://example.com/path", "short.url", { hashAlgorithm: "custom", customHashFn: (url) => { let hash = 0; for (let i = 0; i < url.length; i++) { hash = (hash * 31 + url.charCodeAt(i)) & 0xffffffff; } return hash; }, }); ``` ### Decoding a Short URL #### Basic decoding example ```typescript import { decodeUrl } from "@unitio-code/url-shortener"; // Decode a short URL to get the original URL const originalUrl = await decodeUrl("short.url/r/Ab3x7Z"); console.log(originalUrl); // Output: https://example.com/very/long/path ``` ## Advanced Usage ### Storage and Persistence By default, the library uses in-memory storage (`MemoryStorage`). For production environments, it is recommended to use a persistent storage like Redis. #### Using Redis Storage ```typescript import { createShortUrl, RedisStorage } from "@unitio-code/url-shortener"; import Redis from "ioredis"; const redisClient = new Redis("redis://localhost:6379"); const storage = new RedisStorage(redisClient); const shortUrl = await createShortUrl("https://example.com", "short.url", { storage: storage }); // When decoding, you must also provide the same storage const originalUrl = await decodeUrl(shortUrl, storage); ``` ### TTL (Time to Live) You can set an expiration time for your shortened URLs in seconds. ```typescript const temporaryUrl = await createShortUrl("https://example.com", "short.url", { ttl: 3600 // Expires in 1 hour }); ``` ### Customizing URL Structure #### Without redirect path segment ```typescript const noRedirectPath = await createShortUrl("https://example.com/path", "short.url", { includeRedirectPath: false, }); // Output: short.url/Ab3x7Z ``` #### With custom redirect path segment ```typescript const customPath = await createShortUrl("https://example.com/path", "short.url", { redirectPathSegment: "goto", }); // Output: short.url/goto/Ab3x7Z ``` ## How It Works 1. **Validation**: The library validates the input URL to ensure it's a properly formatted URL. 2. **Hash Generation**: A hash is generated from the input URL using the selected algorithm. 3. **Collision Detection**: If the hash already exists in storage for a different URL, the library automatically re-hashes with an offset until a unique hash is found (up to `maxRetries`). 4. **Encoding**: The numeric hash is encoded using base62 (A-Z, a-z, 0-9) to create a short code. 5. **Storage**: The mapping is stored in the provided storage backend (Memory or Redis) with an optional TTL. 6. **URL Building**: The final short URL is constructed using the specified domain and path options. ## API Reference ### `createShortUrl(longUrl: string, domain: string, options?: CreateShortUrlOptions): Promise<string>` Creates a short URL from a long URL. - `longUrl`: The original URL to shorten. - `domain`: Domain name for the short URL. - `options`: Optional configuration (see [Customization Options](#customization-options)). - **Returns**: A Promise that resolves to the shortened URL string. ### `decodeUrl(shortUrl: string, storage?: StorageInterface): Promise<string | undefined>` Decodes a short URL back to its original URL. - `shortUrl`: The short URL to decode. - `storage`: Optional storage instance to look up the mapping. - **Returns**: A Promise that resolves to the original URL if found, or undefined. ### `encodeId(id: number): string` Encodes a numeric ID to a base62 string. - `id`: The numeric ID to encode. - **Returns**: A base62 encoded string. ### `decodeShortUrl(shortCode: string): number` Decodes a base62 encoded short code back to its numeric ID. - `shortCode`: The base62 encoded string. - **Returns**: The numeric ID. ### `buildShortUrl(id: number, options: ShortUrlOptions): string` Builds a complete short URL from a numeric ID and options. - `id`: The numeric ID to encode. - `options`: Configuration options for the short URL (domain is required). - **Returns**: The complete short URL string. ### `RedisStorage(client: any)` Storage class for Redis integration. Expects a client compatible with `ioredis`. ### `MemoryStorage()` The default in-memory storage implementation. ### `isValidUrl(url: string): boolean` Validates if a string is a properly formatted URL. - `url`: The URL string to validate. - **Returns**: True if the URL is valid, false otherwise. ## Production Considerations ### Storage Backends - **Memory Storage**: Fast but non-persistent. Data is lost when the application restarts. - **Redis Storage**: Persistent, fast, and supports TTL. Recommended for production. ### Collision Handling The library automatically handles hash collisions by: 1. Detecting when a hash already exists for a different URL 2. Re-hashing with an offset (adding attempt number to the input) 3. Retrying up to `maxRetries` times (default: 10) ### URL Validation All URLs are validated before processing. Invalid URLs will throw an error. ## License ISC