Summary
piik.me currently creates permanent short links with no expiration mechanism. For marketing campaigns, time-limited promotions, or security-conscious users who want short links to stop working after a certain date, there is no way to set an expiry on a link. This is a standard feature in every major URL shortener (Bitly, TinyURL, Rebrandly) and is a meaningful differentiator for a "professional-grade" platform.
Problem
- All links created via
POST /api/shorten are permanent — there is no expiresAt field in the links Firestore collection schema.
- Users who share links for time-sensitive content (event registrations, limited-time offers, one-time download links) have no way to deactivate a link automatically.
- From a security perspective, short links to internal resources shared temporarily (e.g., a 24-hour Zoom link) remain permanently accessible even after the underlying resource is gone.
- The
GET /:shortCode redirect handler has no TTL check — even if a user manually wanted to set an expiry, there is no enforcement.
- Competitor analysis: Bitly's free tier supports link expiration; piik.me has no equivalent.
Proposed Solution
1. Update the Firestore links schema to include expiresAt:
The links collection document gains an optional field:
{
originalUrl: string,
shortCode: string,
shortUrl: string,
userId: string,
userEmail: string,
createdAt: timestamp,
expiresAt: timestamp | null, // NEW — null means never expires
utmParams: { ... }
}
2. Update POST /api/shorten to accept expiresAt:
// src/routes/links.routes.js (or equivalent)
router.post('/shorten', authMiddleware, async (req, res) => {
const { originalUrl, customCode, expiresIn, utmParams } = req.body;
// expiresIn: optional, in hours (e.g., 24 = expires in 24 hours)
let expiresAt = null;
if (expiresIn && Number.isInteger(expiresIn) && expiresIn > 0 && expiresIn <= 8760) {
// Cap at 1 year (8760 hours)
expiresAt = new Date(Date.now() + expiresIn * 60 * 60 * 1000);
}
// ... existing link creation logic ...
await db.collection('links').doc(shortCode).set({
// ... existing fields ...
expiresAt: expiresAt ? admin.firestore.Timestamp.fromDate(expiresAt) : null,
});
});
3. Add expiry check in the redirect handler:
// server.js or routes/redirect.routes.js
app.get('/:shortCode', async (req, res) => {
const { shortCode } = req.params;
// Skip bio link slugs and API routes
if (shortCode.startsWith('api') || shortCode.startsWith('u')) {
return next();
}
const linkDoc = await db.collection('links').doc(shortCode).get();
if (!linkDoc.exists) {
return res.status(404).send('Link not found.');
}
const link = linkDoc.data();
// Check expiry
if (link.expiresAt && link.expiresAt.toDate() < new Date()) {
return res.status(410).send('This link has expired.');
}
// ... existing redirect + analytics tracking logic ...
res.redirect(link.originalUrl);
});
4. Frontend UI changes in public/js/app.js:
- Add an optional "Link Expiration" dropdown in the create-link form:
[ Never ] [ 24 hours ] [ 7 days ] [ 30 days ] [ Custom date ]
- Show the expiry date/time in the link dashboard if set.
- Display an "EXPIRED" badge on expired links in the user's link list.
Additional Notes
- The 410 Gone HTTP status code is the semantically correct response for expired/deleted resources — preferable to 404 because it signals the resource existed but is intentionally no longer available.
expiresAt: null is the default — all existing links are unaffected by this change.
- A Firestore scheduled Cloud Function (or a Vercel cron job) could batch-delete expired links nightly to keep the database clean — this is a separate follow-up.
Could you assign this issue to me?
Labels: enhancement, feature, backend, GSSoC 2026
Summary
piik.me currently creates permanent short links with no expiration mechanism. For marketing campaigns, time-limited promotions, or security-conscious users who want short links to stop working after a certain date, there is no way to set an expiry on a link. This is a standard feature in every major URL shortener (Bitly, TinyURL, Rebrandly) and is a meaningful differentiator for a "professional-grade" platform.
Problem
POST /api/shortenare permanent — there is noexpiresAtfield in thelinksFirestore collection schema.GET /:shortCoderedirect handler has no TTL check — even if a user manually wanted to set an expiry, there is no enforcement.Proposed Solution
1. Update the Firestore
linksschema to includeexpiresAt:The
linkscollection document gains an optional field:2. Update
POST /api/shortento acceptexpiresAt:3. Add expiry check in the redirect handler:
4. Frontend UI changes in
public/js/app.js:[ Never ] [ 24 hours ] [ 7 days ] [ 30 days ] [ Custom date ]Additional Notes
expiresAt: nullis the default — all existing links are unaffected by this change.Could you assign this issue to me?
Labels:
enhancement,feature,backend,GSSoC 2026