Skip to main content

URL Shortener

๐Ÿ“– Overviewโ€‹

Middleware Path: internal/api/v1/url-shortener/redirector.js

The URL Shortener provides short URL creation and redirection with visit tracking. Core responsibilities include:

  • URL Redirection: Redirect short codes to target URLs
  • Visit Tracking: Increment visit counter on each redirect
  • URL Validation: Ensure proper URL format with protocol
  • 404 Handling: Handle invalid or expired short codes

๐Ÿ—„๏ธ Collections Usedโ€‹

  • short-urls (link removed - file does not exist) - Short URL mappings
    • code (String) - Unique short code
    • url (String) - Target URL
    • visit (Number) - Visit counter
    • account_id (ObjectId) - Owner account
    • created_by (ObjectId) - Creator user

๐Ÿ”„ Data Flowโ€‹

URL Redirection Flowโ€‹

sequenceDiagram
participant User
participant Middleware
participant ShortURL
participant Database
participant TargetSite

User->>Middleware: GET /:code
Note over User,Middleware: Example: GET /abc123

Middleware->>Database: Find short URL by code
Database-->>Middleware: Short URL document

alt Code Not Found
Middleware-->>User: 404 Not Found
else Code Found
Middleware->>Database: Increment visit counter
Database-->>Middleware: Updated

Middleware->>Middleware: Validate URL format
Note over Middleware: Add https:// if missing

Middleware->>TargetSite: Redirect 302
TargetSite-->>User: Target page
end

style Middleware fill:#e3f2fd
style Database fill:#fff4e6

Visit Trackingโ€‹

flowchart TD
A[Incoming Request] --> B[Extract code from URL]
B --> C{Code exists?}

C -->|No| D[Return 404 Not Found]
C -->|Yes| E[Increment visit counter]

E --> F{URL has protocol?}
F -->|Yes| G[Use URL as-is]
F -->|No| H[Add https:// prefix]

G --> I[302 Redirect to target]
H --> I

D --> J[End]
I --> J

style E fill:#e8f5e9
style I fill:#e3f2fd

๐Ÿ”ง Business Logic & Functionsโ€‹

redirector(req, res)โ€‹

Purpose: Middleware function to redirect short URLs to target URLs with visit tracking

Parameters:

  • req (Object) - Express request object
    • req.params.code (String) - Short URL code from route parameter
  • res (Object) - Express response object

Returns: HTTP redirect (302) or 404 error

Business Logic Flow:

  1. Find Short URL by Code

    const ShortURL = require('../models/short-url');

    const shortUrl = await ShortURL.findOne({
    code: req.params.code,
    });

    if (!shortUrl) {
    return res.status(404).send('Short URL not found');
    }
  2. Increment Visit Counter

    await ShortURL.updateOne({ _id: shortUrl._id }, { $inc: { visit: 1 } });
  3. Validate and Normalize URL

    let targetUrl = shortUrl.url;

    // Add protocol if missing
    if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) {
    targetUrl = 'https://' + targetUrl;
    }
  4. Redirect to Target URL

    return res.redirect(302, targetUrl);

Route Integration:

// Route definition in routes file
router.get('/:code', redirector);

Request Example:

GET /abc123 HTTP/1.1
Host: short.dashclicks.com

Response Example:

HTTP/1.1 302 Found
Location: https://example.com/long-url-path

Key Business Rules:

  • Visit Tracking: Counter incremented atomically before redirect
  • Protocol Normalization: HTTPS added if protocol missing
  • 404 on Missing: Returns 404 if code doesn't exist
  • No Authentication: Public endpoint, no auth required
  • No Expiration: Short URLs don't expire automatically

๐Ÿ”€ Integration Pointsโ€‹

Create Short URL:

const ShortURL = require('../models/short-url');
const shortid = require('shortid');

// Create short URL
const shortUrl = await ShortURL.create({
code: shortid.generate(), // Generate unique code
url: 'https://example.com/long-url',
account_id: accountId,
created_by: userId,
visit: 0,
});

// Return short link
const shortLink = `https://short.dashclicks.com/${shortUrl.code}`;

Analytics Moduleโ€‹

Track Click Analytics:

// Get visit statistics
const shortUrl = await ShortURL.findOne({ code: 'abc123' });

console.log(`Total visits: ${shortUrl.visit}`);

Email/SMS Campaignsโ€‹

Use Short URLs in Messages:

// Replace long URLs with short URLs in email
const emailBody = `
Click here: https://short.dashclicks.com/${shortUrl.code}
`;

// Track clicks from campaign
const campaignClicks = await ShortURL.aggregate([
{
$match: {
campaign_id: campaignId,
},
},
{
$group: {
_id: null,
total_clicks: { $sum: '$visit' },
},
},
]);

๐Ÿงช Edge Cases & Special Handlingโ€‹

Missing Protocol in Target URLโ€‹

Auto-Add HTTPS:

let targetUrl = shortUrl.url;

if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) {
targetUrl = 'https://' + targetUrl;
}

// Before: example.com
// After: https://example.com

Invalid Short Codeโ€‹

404 Response:

const shortUrl = await ShortURL.findOne({ code: req.params.code });

if (!shortUrl) {
return res.status(404).send('Short URL not found');
}

Race Condition on Visit Counterโ€‹

Atomic Increment:

// Use $inc operator for atomic counter increment
await ShortURL.updateOne({ _id: shortUrl._id }, { $inc: { visit: 1 } });

// This prevents race conditions when multiple users
// click the same short URL simultaneously

Malformed Target URLsโ€‹

Potential Issue:

// Edge case: Invalid URL after normalization
targetUrl = 'javascript:alert("XSS")';

// Current implementation doesn't validate URL scheme
// Consider adding validation:
if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) {
return res.status(400).send('Invalid target URL');
}

โš ๏ธ Important Notesโ€‹

  1. No Authentication: Redirect endpoint is public and doesn't require authentication. Anyone with the code can access the link.

  2. Visit Counter: Incremented on every request, including bots, crawlers, and duplicate visits from same user.

  3. 302 Redirect: Uses temporary redirect (302) instead of permanent (301), allowing URL changes if needed.

  4. Protocol Assumption: Defaults to HTTPS when protocol missing. HTTP URLs must explicitly include http://.

  5. No Expiration: Short URLs don't have built-in expiration. Consider adding expires_at field for time-limited links.

  6. No Click Analytics: Only tracks visit count, not detailed analytics (timestamp, referrer, user agent, location).

  7. No Rate Limiting: No rate limiting on redirects. Consider adding to prevent abuse.

  8. Code Generation: Short codes generated elsewhere (typically using shortid or similar library).

  9. Case Sensitive: Short codes are case-sensitive (ABC123 โ‰  abc123).

  10. No Preview: Direct redirect without preview page. Consider adding preview mode for security.

  11. Error Handling: Minimal error handling. Database errors may cause 500 responses.

  12. Performance: Single database query per redirect. Consider caching popular short URLs.

๐Ÿš€ Enhancement Opportunitiesโ€‹

Advanced Analyticsโ€‹

// Track detailed click data
await ShortURL.updateOne(
{ _id: shortUrl._id },
{
$inc: { visit: 1 },
$push: {
clicks: {
timestamp: new Date(),
ip: req.ip,
userAgent: req.headers['user-agent'],
referrer: req.headers['referer'],
},
},
},
);
// Check expiration before redirect
if (shortUrl.expires_at && shortUrl.expires_at < new Date()) {
return res.status(410).send('This link has expired');
}

Custom Domainsโ€‹

// Support multiple short domains
const domain = req.hostname;
const shortUrl = await ShortURL.findOne({
code: req.params.code,
domain: domain,
});

Preview Modeโ€‹

// Show preview page instead of direct redirect
if (req.query.preview === 'true') {
return res.render('preview', {
targetUrl: shortUrl.url,
code: shortUrl.code,
});
}
  • Link Management (link removed - file does not exist) - Short URL creation and management
  • Analytics Module (link removed - file does not exist) - Click tracking and reporting
  • Campaigns (link removed - file does not exist) - Short URL usage in campaigns
  • Security Practices (link removed - file does not exist) - URL validation and XSS prevention
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM