@shyamraghuonec/link-shortener
v0.1.1
Published
A self-hosted link shortener component for Convex with click analytics and UTM tracking.
Maintainers
Readme
🔗 Convex Link Shortener Component
A self-hosted link shortener component for Convex. Create short links with analytics, password protection, and UTM tracking.
✨ Features
- 🔗 Create/update/revoke short links
- 🔒 Password protection
- 📊 Click analytics with geo tracking
- 🏷️ UTM parameter handling (capture/strip/passthrough)
- ⏰ Expiration dates
📦 Installation
npm install @shyamraghuonec/link-shortener🚀 Quick Start
1. Add to Your App
// convex/convex.config.ts
import { defineApp } from "convex/server";
import linkShortener from "@shyamraghuonec/link-shortener/convex.config.js";
const app = defineApp();
app.use(linkShortener);
export default app;2. Create Links
// convex/links.ts
import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { LinkShortener } from "@shyamraghuonec/link-shortener";
import { v } from "convex/values";
const linkShortener = new LinkShortener(components.linkShortener);
export const create = mutation({
args: { destinationUrl: v.string() },
handler: async (ctx, args) => {
return await linkShortener.create(ctx, {
destinationUrl: args.destinationUrl,
});
},
});
export const get = query({
args: { code: v.string() },
handler: async (ctx, args) => {
return await linkShortener.getByCode(ctx, args.code);
},
});3. Set Up Redirects
// convex/http.ts
import { httpRouter } from "convex/server";
import { registerRedirectRoutes } from "@shyamraghuonec/link-shortener";
import { components } from "./_generated/api";
const http = httpRouter();
registerRedirectRoutes(http, components.linkShortener, {
pathPrefix: "/r",
});
export default http;Now your-site.convex.site/r/abc123 redirects to the destination!
📖 API
LinkShortener Class
const linkShortener = new LinkShortener(components.linkShortener);
// Create a link
await linkShortener.create(ctx, {
destinationUrl: "https://example.com",
code: "my-link", // optional custom code
password: "secret", // optional password
expiresAt: Date.now() + 86400000, // optional expiry
});
// Get link
await linkShortener.getByCode(ctx, "my-link");
// List links
await linkShortener.list(ctx, { limit: 10 });
// Update
await linkShortener.update(ctx, linkId, { destinationUrl: "https://new-url.com" });
// Revoke
await linkShortener.revoke(ctx, linkId);
// Analytics
await linkShortener.summary(ctx, linkId);
await linkShortener.clicksOverTime(ctx, linkId, { days: 7 });
await linkShortener.topReferrers(ctx, linkId);
await linkShortener.geoBreakdown(ctx, linkId);🔒 Password Protection
// Create password-protected link
await linkShortener.create(ctx, {
destinationUrl: "https://secret.com",
password: "mysecret",
});
// Resolve with password
await linkShortener.resolve(ctx, "abc123", "mysecret");Set up password page in HTTP routes:
registerRedirectRoutes(http, components.linkShortener, {
pathPrefix: "/r",
passwordPageUrl: "https://your-site.convex.site/password",
});📄 License
Apache-2.0
