Creating Firebase dynamic links with Firebase cloud functions

Cloud functions for Firebase have been around for a couple of months now, though they are still in beta. I decided to move some code that I had used to create dynamic links from a client android app to cloud functions for Firebase.

This is because I faced a few challenges;

  • when one would tap/click the share button in the app, the long dynamic link would be generated and then use the dynamic link rest API to send a request with the long link.
  • The API would respond with the short link which would then be shared. This worked for a few cases where the network/internet was stable and good otherwise, generating the short dynamic link would fail and the user would end up sending the long dynamic link which was undesirable.

This is why I moved the code to cloud functions for Firebase. I am going to walk you through how I did it.

Setting up

Cloud functions for Firebase are written in Nodejs, therefore we need to setup our development environment. If you do not have node installed on your machine, head over to the Nodejs website and choose your platform/OS for instructions on how to install it. After you are done, check whether its installed correctly by running node -v in your terminal. This will output your node version. For example, mine is v6.9.2. Nodejs ships bundled with npm which is its package manager . We shall use it to install the dependencies we need. Now that we have node installed let us set up our cloud functions.

Firstly, install Firebase tools using this command in the terminal which installs the firebase command globally.

npm install -g firebase-tools

After installation, initialize your project by running firebase login which opens up a browser for signing into the google account associated with your Firebase project.

Run firebase init functions to initialize the functions and also install the required dependencies using npm.

Choose a project from your list of Firebase projects or create a new one

Then install the dependencies required by the functions to run

After initialization, the folder structure should look like this:

myproject
+- .firebaserc # Hidden file that helps you quickly switch between
| # projects with `firebase use`
|
+- firebase.json # Describes properties for your project
|
+- functions/ # Directory containing all your functions code
|
+- package.json # npm package file describing your Cloud Functions code
|
+- index.js # main source file for your Cloud Functions code
|
+- node_modules/ # directory where your dependencies (declared in
# package.json) are installed

Getting started

You can find all the code in this repository on Github.

Open the myproject folder in a text editor or IDE of your choice, I usually use VSCode or Webstorm.

From the text editor open the index.js file. This is where the code will be written.

We are going to use an example of a blog post. The post has a title and the body. All blog posts are to be stored in the Firebase realtime database, this also enables us to use the database triggers in cloud functions.

Below is the structure of the database

Posts
<postId>
title: "blog post title"
body: "Lorem ipsum lorem ispum"

Add a shareUrl attribute to the blog post that will hold the dynamic link.

Back in the index.js file, initialize firebase functions as shown below

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

We can now listen for a database trigger at this location “Posts/{postId}”.

exports.postDynamicLink = functions.database.ref('Posts/{postId}')
.onWrite(event => {
let post = event.data.val();
const postId = event.params.postId;
});

postDynamicLink is the name of the function and functions.database.ref .onwrite enables us listen for the firebase trigger at ‘posts/{postId}’ path.

From the event we can get the post details pushed to the database and also the postId. With event.data.val() we are able to get the post details and event.params.postId get the post Id.


Long dynamic link

Now that we have the post data and post Id, we can construct the long dynamic link which we shall send via the app links API and receive back a short dynamic link. You can refer to the official documentation for the different fields of the long dynamic link.

To easily construct a url, I installed a package called build-url using npm. To install it run npm install --save build-url inside your functions folder. Like any other, include it as required at the top of the index.js file const urlBuilder = require(‘build-url’);

Let’s create a helper function called makeDynamicLongLink where we are going to construct the long link.

function makeDynamicLongLink(postId, socialDescription, socialImageUrl) {
return urlBuilder(`${functions.config().applinks.link}`, {
queryParams: {
link: "https://www.arvana.io/code/" + postId,
apn: "io.arvana.blog",
dfl: "https://www.arvana.io",
st: "Arvana Blog - All you need to know about arvana",
sd: socialDescription,
si: socialImageUrl
}
});
}

We use the urlBuilder package we required above the file to build the long dynamic code. Firebase cloud functions provide us a way of using environment variable, so in this line `${functions.config().applinks.link}` we are storing our appLink url from the Firebase console within the firebase environment config. You can get your root dynamic link url by logging into your firebase console and the clicking Dynamic Links option in the left menu, later we shall see how to save it to the firebase functions config.

Image showing the root dynamic link

When constructing my long link, I use only 6 parameters but there are a ton of them, please refer to the official documentation for the full list.

Now that we have the long dynamic url, let’s use the dynamic link Rest API to get the short url. Before we can send a request to the Rest API, we need to install two more modules request and request-promise which we are going to use to send the request and then receive a promise back which we can then chain with other promises that will write the short url to the realtime database.

So as usual run npm install --save request request-promise than require it at the top of the file const request = require('request-promise’) We also need to get our API key which will authorize our request, from the Firebase console by clicking the settings gear then choosing project settings from the interface you will see web api key copy it and save it somewhere for now as we shall add it to our config later.

Using the requests module we send a request to the dynamics link REST API as shown in the code snippet below. We then get back a promise containing the response body from which we get the short url and return it so that we use it in the next promise to set it in the database.

const socialDescription = `Arvana Blog - ${post.title}`;
const socialImageUrl = "http://res.cloudinary.com/jokam/image/upload/v1498378886/ar_blog_qeqjzu.png";

const options = {
method: 'POST',
uri: `https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=${functions.config().applinks.key}`,
body: {
"longDynamicLink": makeDynamicLongLink(postId, socialDescription, socialImageUrl)
},
json: true
};

request(options)
.then(function (parsedBody) {
console.log(parsedBody);
return parsedBody.shortLink;
})

We then set the short dynamic link in the real time database as shown in the code snippet below.

request(options)
.then(function (parsedBody) {
console.log(parsedBody);
return parsedBody.shortLink;
})
.then((shortLink) => {
post.shareUrl = shortLink;
console.log('short link: ' + shortLink);
return event.data.ref.set(post);
})

The set method also returns a promise; promises enable the cloud functions to be activated, so to speak, until all the work is done otherwise we risk not saving our data.

At this stage, we are done, BUT if we save and upload our function we shall run into an infinite loop because we are listening for changes at the same path we are updating. This would use up our free quota and also increase our costs of running the function. In order to prevent this, we can add a field to our post and then check whether it is True before we can proceed with generating the dynamic link. If it is True we return out of the function as shown below.

let post = event.data.val();
const postId = event.params.postId;

if (post.addedDynamicLink) {
return;
}

post.addedDynamicLink = true;

At this point your code should look like this:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const urlBuilder = require('build-url');
const request = require('request-promise');

admin.initializeApp(functions.config().firebase);

exports.postDynamicLink = functions.database.ref('Posts/{postId}')
.onWrite(event => {
let post = event.data.val();
const postId = event.params.postId;

if (post.addedDynamicLink) {
return;
}

post.addedDynamicLink = true;

const socialDescription = `Arvana Blog - ${post.title}`;
const socialImageUrl = "http://res.cloudinary.com/jokam/image/upload/v1498378886/ar_blog_qeqjzu.png";

const options = {
method: 'POST',
uri: `https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=${functions.config().applinks.key}`,
body: {
"longDynamicLink": makeDynamicLongLink(postId, socialDescription, socialImageUrl)
},
json: true
};

request(options)
.then(function (parsedBody) {
console.log(parsedBody);
return parsedBody.shortLink;
})
.then((shortLink) => {
post.shareUrl = shortLink;
console.log('short link: ' + shortLink);
return event.data.ref.set(post);
})

});

function makeDynamicLongLink(postId, socialDescription, socialImageUrl) {
return urlBuilder(`${functions.config().applinks.link}`, {
queryParams: {
link: "https://www.arvana.io/code/" + postId,
apn: "io.arvana.blog",
dfl: "https://www.arvana.io",
st: "Arvana Blog - All you need to know about arvana",
sd: socialDescription,
si: socialImageUrl
}
});
}

Setting the functions environment config

As I told you earlier, we would be adding our dynamic link root url link and web api key to our functions config. Run this in your terminal from project folder.

firebase functions:config:set applinks.key="AIzaSyAeUvO-HteeUvCgYL7vmhSB09mpR_GPP8E" applinks.link="https://f8c7y.app.goo.gl/"

Which will output

+  Functions config updated.
Please deploy your functions for the change to take effect by running firebase deploy --only functions

We are now ready to upload our function.


Deploying the function

To deploy our function to our firebase project we just have to run firebase deploy From your Firebase dashboard under functions you will see your deployed function as shown below.

Firebase functions dashboard

Testing

Time to check the fruit of our efforts. Head over to the database section of the dashboard to test out our function. Add the Posts node then add the postId plus the title and body After adding them, the cloud function will run and the short dynamic link will be added. Add dummy data to test it out.

Your database should now look like this

Firebase realtime database

Social Media

When the short link is shared on social media, the info and image we added are displayed. For example Facebook renders it as shown below

Facebook Timeline post

Code

You can find all the code in this repository on Github.

If you have found this article useful hit the clap button to recommend it so that others may see it.