
Generating Short URLs with Firebase: Replacing Firebase Dynamic Links
With the announced deprecation of Firebase Dynamic Links in 2025, it’s the perfect time to explore alternatives for creating and managing short URLs. This article guides you through the process of creating short URLs using Firebase Firestore and Cloud Functions, and explains how to receive email notifications with access details.
Introduction
Short URLs are shortened versions of longer URLs. They are useful for simplifying and monitoring shared links, improving link aesthetics, and making link management easier. Firebase, a Google platform for web and mobile app development, offers a solution to generate and manage short URLs without relying on Firebase Dynamic Links.

Setting Up Your Firebase Project
To begin, you’ll need a Firebase project. If you don’t have one already, create one by following these steps:
- Access Firebase Console: Go to the
Firebase Console
.
- Create a New Project: Click on “Add Project” and follow the instructions to set up your Firebase project.
Creating the Data Structure
We’ll use Firestore to store short URLs and access information. Here’s the proposed structure:
shortUrls Collection:
- Document: shortUrlId (unique ID for the short URL)
- Fields:
- longUrl: Original URL
shortUrls/{shortUrlId}/accessLogs Collection:
- Document: logId (unique ID for each access)
- Fields:
- accessedAt: Timestamp of access
- userAgent: Browser User-Agent
- referrer: URL referrer
- ip: Visitor’s IP address
Cloud Functions for Handling Accesses
We’ll use a Cloud Function to listen for changes in the accessLogs collection and send an email when a short URL is accessed.
Cloud Function Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
| const functions = require("@google-cloud/functions-framework");
const admin = require("firebase-admin");
const moment = require("moment-timezone");
const protobuf = require("protobufjs");
const path = require("path");
if (!admin.apps.length) {
admin.initializeApp();
}
const db = admin.firestore();
functions.cloudEvent("sendEmailOnShortUrlClick", async (cloudEvent) => {
console.log(`Function triggered by event on: ${cloudEvent.source}`);
console.log(`Event type: ${cloudEvent.type}`);
try {
const protoPath = path.join(__dirname, "data.proto");
console.log("Loading protos from:", protoPath);
const root = await protobuf.load(protoPath);
const DocumentEventData = root.lookupType(
"google.events.cloud.firestore.v1.DocumentEventData"
);
console.log("Decoding data...");
const firestoreData = DocumentEventData.decode(cloudEvent.data);
const newValue = firestoreData.value.fields;
const pathArray = cloudEvent.subject.split("/");
const shortUrlId = pathArray[pathArray.indexOf("documents") + 2];
if (!shortUrlId) {
console.error(
"shortUrlId is undefined. Check the cloudEvent.subject format."
);
return;
}
console.log(`New Firestore document: ${JSON.stringify(newValue, null, 2)}`);
console.log(`Extracted shortUrlId: ${shortUrlId}`);
const shortUrlDoc = await db.collection("shortUrls").doc(shortUrlId).get();
if (!shortUrlDoc.exists) {
console.error("Short URL document not found.");
return;
}
const shortUrlData = shortUrlDoc.data();
const longUrl = shortUrlData.longUrl;
console.log(`Long URL found: ${longUrl}`);
if (!newValue.accessedAt) {
console.error("accessedAt field is missing in log data.");
return;
}
const accessedAt = new Date(
newValue.accessedAt.timestampValue.seconds * 1000
);
const formattedDate = moment(accessedAt)
.tz("Europe/Rome")
.format("DD/MM/YYYY HH:mm:ss");
console.log(`Formatted access date: ${formattedDate}`);
const emailContent = {
dataCreazione: {
data: admin.firestore.FieldValue.serverTimestamp(),
},
to: "youremail@test.com",
message: {
subject: `Clic su Short URL: ${shortUrlId}`,
html: `
<div style="font-family: Arial, sans-serif; line-height: 1.6;">
<h2>Notifica di Clic su Short URL</h2>
<p>Ciao,</p>
<p>È stato rilevato un clic su uno dei tuoi short URL.</p>
<p><strong>Short URL:</strong> ${shortUrlId}</p>
<p><strong>URL originale:</strong> <a href="${longUrl}">${longUrl}</a></p>
<p><strong>Dettagli del clic:</strong></p>
<ul>
<li><strong>Data e ora:</strong> ${formattedDate}</li>
<li><strong>User-Agent:</strong> ${
newValue.userAgent?.stringValue || "N/A"
}</li>
<li><strong>Referrer:</strong> ${
newValue.referrer?.stringValue || "Direct"
}</li>
<li><strong>Indirizzo IP:</strong> ${newValue.ip?.stringValue || "Unknown"}</li>
</ul>
<p>Puoi visualizzare ulteriori dettagli accedendo al tuo account Firebase.</p>
<p>Grazie,<br/>Il team di Sitiapp</p>
</div>
`,
},
};
await db.collection("mail").add(emailContent);
console.log("Email queued for sending.");
} catch (error) {
console.error("Error processing Firestore event:", error);
}
});
|
Code Explanation
- Configuration and Initialization:
- Initializes the Firebase app and Firestore client.
- Loads the data.proto file to decode Firestore event data.
- Event Processing:
- Decodes event data to extract document details.
- Retrieves shortUrlId and fetches original document data.
- Data Formatting:
- Converts the timestamp to a readable format.
- Prepares the email content with access details.
- Sending Email:
- Adds a document to the mail collection, which will be used to send the email.
Generating Short URLs with the Firebase CLI
You can use a script to generate short URLs and save them in Firestore. Here’s a sample script to add a short URL using the Firebase CLI:
Short URL Generation Script
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/bin/bash
# Variables
PROJECT_ID="your-firebase-project-id"
COLLECTION_NAME="shortUrls"
SHORT_URL_ID=$1
LONG_URL=$2
# Add a short URL to Firestore
firebase firestore:documents:set "projects/$PROJECT_ID/databases/(default)/documents/$COLLECTION_NAME/$SHORT_URL_ID" \
--data '{"longUrl": "'"$LONG_URL"'"}'
echo "Short URL generated and stored in Firestore."
|
Using the Script
- Save the script in a file named generate-short-url.sh.
- Make the file executable:
chmod +x generate-short-url.sh
. - Run the script, passing the shortUrlId and longUrl as parameters:
1
| ./generate-short-url.sh myShortUrlId https://www.example.com
|
Email Notification
When a user accesses a short URL, the Cloud Function sends an email to the specified address with the access details, including:
- Access date and time.
- Device User-Agent.
- URL referrer.
- Visitor’s IP address.
Example of Received Email
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| Subject: Clic su Short URL: myShortUrlId
Notifica di Clic su Short URL
Ciao,
È stato rilevato un clic su uno dei tuoi short URL.
Short URL: myShortUrlId
URL originale: https://www.example.com
Dettagli del clic:
- Data e ora: 26/08/2024 17:35:57
- User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
- Referrer: Direct
- Indirizzo IP: XX.XXX.XXX.XX
Puoi visualizzare ulteriori dettagli accedendo al tuo account Firebase.
Grazie,
Il team di Sitiapp
|
Configuring Firebase Hosting
To make your short URLs accessible through a custom domain, you need to configure Firebase Hosting. Here’s how:
- Enable Hosting: In your Firebase project’s dashboard, go to the “Hosting” section. Click on “Enable Hosting”.
- Configure Public Directory: If needed, modify the “public” directory containing your website’s static files.
- Create a Cloud Function for Redirection: Create a Cloud Function called “redirectShortUrl” that redirects the user to the original URL based on the requested short URL.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
exports.redirectShortUrl = functions.https.onRequest(async (req, res) => {
const shortUrlId = req.path.slice(1);
const shortUrlDoc = await db.collection("shortUrls").doc(shortUrlId).get();
if (!shortUrlDoc.exists) {
res.status(404).send("Short URL not found");
return;
}
const longUrl = shortUrlDoc.data().longUrl;
res.redirect(longUrl);
});
|
- Configure Redirection Rules: In your
firebase.json
file, add redirection rules to handle requests to all short URLs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"hosting": {
"site": "link-sitiapp",
"public": "public",
"rewrites": [
{
"source": "**",
"function": {
"functionId": "redirectShortUrl",
"region": "europe-west1"
}
}
]
}
}
|
- Deploy: Deploy your Firebase project using the
firebase deploy
command.
Now, when a user accesses one of your short URLs, they will be redirected to the original URL.
Important: The custom domain must be associated with your Firebase project for this to work correctly.
Configuring Firebase JSON
The firebase.json
file contains the configurations for your Firebase project, including settings for Hosting, Cloud Functions, and other services. Here’s an example of a firebase.json
file that includes configurations for Hosting and Cloud Functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| {
"hosting": {
"site": "link-sitiapp",
"public": "public",
"rewrites": [
{
"source": "**",
"function": {
"functionId": "redirectShortUrl",
"region": "europe-west1"
}
}
]
},
"functions": {
"source": "functions",
"runtime": "nodejs18",
"ignore": ["**/.git", "**/.DS_Store", "**/node_modules", "**/.env*"],
"predeploy": ["npm install --prefix functions"]
}
}
|
Explanation:
- hosting: Defines the settings for Firebase Hosting.
- site: The name of your website.
- public: The directory containing your website’s static files.
- rewrites: Redirection rules for handling requests.
- functions: Defines the settings for Firebase Cloud Functions.
- source: The directory containing your Cloud Function code.
- runtime: The Node.js version used to execute functions.
- ignore: Files and directories to ignore during deployment.
- predeploy: Commands to execute before deployment.
Conclusion
With Firebase Hosting and Cloud Functions, you can manage your short URLs easily and efficiently. The solution is flexible and integrates seamlessly with Firebase, giving you complete control over the link management process.
This approach allows you to generate short URLs and monitor accesses using Firebase Firestore and Cloud Functions, offering a robust and customizable solution to replace Firebase Dynamic Links. If you have any questions or need further clarification, feel free to contact us!