Add new functionality to your web app quickly with Firebase Extensions

1. Introduction

Goals

In this codelab, you'll add functionality to an online marketplace app with the help of Firebase Extensions. Through this codelab, you'll understand how extensions can help you to spend less time on app development and management tasks.

3b6977f679c67db.png

What you'll build

In this codelab, you'll add the following features to an online marketplace web app:

  • Load images faster to increase user retention
  • Limit entries in your database to improve performance and lower your bill
  • Implement automatic deletion of old user data to enhance user data protection

What you'll learn

  • How to discover extensions for common use cases
  • How to install and configure an extension in your project
  • How to maintain (monitor, update, and uninstall) extensions in your project

This codelab is focused on Firebase Extensions. For detailed information about other Firebase products mentioned in this codelab, refer to the Firebase documentation and other codelabs.

What you'll need

  • A computer with a modern web browser installed (Chrome is recommended)
  • A Google account

2. Create and set up a Firebase project

Create a Firebase project

  1. In the Firebase console, click Add project, and name the Firebase project FriendlyMarket.
  2. Click though the project creation options. Accept the Firebase terms. Skip setting up Google Analytics, because you won't use Analytics in this app.
  3. Wait for the project to be provisioned, and then click Continue.

The application that you'll build uses a few Firebase products available for web apps:

  • Firebase Authentication to easily identify your users
  • Firebase Realtime Database to save structured data in the cloud and get instant notification when the data updates
  • Cloud Storage for Firebase to save images in the cloud

You'll now enable and configure those Firebase products, using the Firebase console.

Enable email login

Although authentication isn't the focus of this codelab, it's important to have some form of authentication in your app, to uniquely identify everyone who uses it. You'll use email login.

  1. In the Firebase console, click Develop in the left panel.
  2. Click Authentication, and then click the Sign-in method tab (or click here to go directly to the Sign-in method tab).
  3. Click Email/Password in the Sign-in providers list, set the Enable switch to the on position, and then click Save.

ed0f449a872f7287.png

Enable Realtime Database

The app uses the Firebase Realtime Database to save items for sale.

  1. In the Develop section in the left panel of the Firebase console, click Database.
  2. Scroll down the page past the Cloud Firestore pane, and click Create database in the Realtime Database pane.

cf8de951d2ab2e94.png

  1. Select Start in locked mode, and then click Enable.

Set security rules

Now, you'll set the security rules needed for this app. These are some basic example rules to help secure your app. These rules allow anyone to view items for sale, but these rules allow only signed-in users to perform other reads and writes. Don't worry about the specifics of these rules; you're just going to copy and paste them to get your app up and running.

  1. At the top of the Realtime Database dashboard, click the Rules tab.

e233a24a38b37e95.png

  1. Copy and paste the following rule set into the rules field in the Rules tab:
{
  "rules": {
    ".read": false,
    ".write": false,
      "drafts": {
        ".indexOn": "seller",
        ".read": "auth.uid !== null",
        ".write": "auth.uid !== null"
    },
    "sellers": {
        ".read": "auth.uid !== null",
        ".write": "auth.uid !== null"
    },
      "forsale": {
        ".read": true,
        ".write": "auth.uid !== null"
      }
  }
}
  1. Click Publish.

Enable Cloud Storage

The app uses Cloud Storage for Firebase to save images of items for sale.

  1. In the Develop section in the left panel of the Firebase console, click Storage.
  2. Click Get started.

889013b9c7b8897c.png

  1. Accept the defaults for creating your default storage bucket (click Next, keep the default location, and click Done).

Now, you'll set the security rules needed for this app. These rules only allow authenticated users to post new images, but they allow anyone to view the image for a listed item.

  1. At the top of the Storage dashboard, click the Rules tab.

e7003646b429500b.png

  1. Copy and paste the following rule set into the rules field in the Rules tab:
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
    
    match /friendlymarket/{ImageId} {
      allow read;
      allow write: if request.auth != null;
    }
    
  }
}
  1. Click Publish.

3. Run the sample app

Fork the StackBlitz project

In this codelab, you build and deploy an app using StackBlitz, an online editor that has several Firebase workflows integrated into it. Stackblitz requires no software installation or special StackBlitz account.

StackBlitz lets you share projects with others. Other people who have your StackBlitz project URL can see your code and fork your project, but they can't edit your StackBlitz project.

  1. Go to this URL for the starting code: https://stackblitz.com/edit/friendlymarket-codelab.
  2. At the top of the StackBlitz page, click Fork.

22c44cf92ed26208.png

You now have a copy of the starting code as your own StackBlitz project. Because you didn't sign in, your "account" is called @anonymous, but that's OK. The project has a unique name, along with a unique URL. All of your files and changes are saved in this StackBlitz project.

Add a Firebase Web App to the project

  1. In StackBlitz, view your src/firebase-config.js file. This is where you'll add the Firebase configuration object.
  2. Back in the Firebase console, navigate to your project's overview page by clicking Project Overview in the top left.
  3. In the center of your project's overview page, click the web icon 58d6543a156e56f9.png to create a new Firebase Web App. 88c964177c2bccea.png
  4. Register the app with the nickname FriendlyMarket Codelab.
  5. For this codelab, don't check the box next to Also set up Firebase Hosting for this app. You're going to use the StackBlitz preview pane instead.
  6. Click Register app.
  7. Copy your app's Firebase configuration object to your clipboard. Don't copy the <script> tags. Note: If you need to find the configuration later, follow the instructions here.

6c0519e8f48a3a6f.png

  1. Click Continue to console.

Add your project's configuration to your app:

  1. Back in StackBlitz, go to the src/firebase-config.js file.
  2. Paste your configuration snippet in the file. After you do, it should look like this (but with your own project's values in the configuration object):

177602cbe84f873d.png

What's the starting point for this app?

Take a look at the interactive preview on the right side of the StackBlitz screen:

f3ec800f27fa49b7.png

This codelab starts you off with the code for a basic marketplace app. Any user can view a list of items for sale and click a link to view an item's details page. If a user is signed in, they'll see the seller's contact information so that they can negotiate a price and buy the item.

Try the app:

  1. Sign in with the button at the top of the home screen. You can use a fake email address, name, and password.
  2. Click the Sell something button in the bottom right corner to create a listing.
  3. For Title, enter Xylophone.
  4. For Asking Price, enter 50.
  5. For Item Description, enter the following: This high quality xylophone can be used to play music.
  6. Download this image of a xylophone to your computer, and upload it with the PICTURE OF YOUR ITEM button.

  1. After you fill out all the fields and upload an image, click Post.
  2. Find your new listing. Click your item to see its details screen, and then expand the Seller contact information panel.
  3. Go back to the Firebase console. In the Database dashboard, you'll now see an entry for the item you posted under the forsale node. In the Storage dashboard, you'll also find the image you uploaded in the friendlymarket path.

4. Find and install an extension

The problem

After doing some user research for your app, you find out that most users visit your site from their smartphone, not their desktop. However, your stats also show that mobile users tend to leave your site ("churn") after just a few seconds.

Curious, you test your site with mobile connection speeds. (Learn how to do that here.) You find that the images take a very long time to load and aren't cached in the browser at all. That long load time is incurred on every page view!

The solution

After reading up on how to optimize images, you decide to take two steps to improve the image loading performance:

  • Compress images. Even mobile phones take images with far higher resolution than is necessary for the needs of this app. Reducing the file size will speed up load times without a noticeable drop in resolution in the app.
  • Caching. By default, Cloud Storage objects have headers that tell browsers not to cache images, meaning that a user's browser will re-download the same image over and over again! Luckily, you can change these headers to allow caching. Both the client-side Cloud Storage SDK and the Firebase Admin SDK allow you to set these headers.

To compress images, you'll need to either limit upload quality or have a server-side process that resizes images. Let's consider the tradeoffs:

  • Client-side. For a client-side process, you could just limit the file size for uploaded images. This means that you don't need to write or maintain any new server logic. However, it also means that your sellers need to figure out how to resize their own images, which is a painful and unintuitive barrier to creating a new listing.
  • Server-side. If you use Cloud Functions for Firebase, you can trigger a function that automatically resizes an image on upload. This means that sellers can upload whatever size of image they like (no extra work for them), and your backend function can seamlessly resize the image. There's even a sample available for this function!

It sounds like server-side is the way to go. But this idea still involves cloning the sample, following its setup instructions, and then deploying the function with the Firebase CLI. Resizing images sounds like such a common use case. Isn't there an easier solution?

An easier solution

You're in luck. There is an easier solution: Firebase Extensions! Let's check the catalog of available extensions on the Firebase website.

e6bc3874cf23f34f.png

Look at that! There's an extension called "Resize Images". That looks promising.

Let's use this extension in your app!

Install an extension

  1. Click See details to view more information about this extension. Under What you can configure, the extension lets you set the dimensions you'd like to resize to, and you can even set the cache header. Perfect!
  2. Click the Install in console button on the extension's details page. You will be taken to a Firebase console page which lists all of your projects.
  3. Choose the FriendlyMarket project that you created for this codelab.
  4. Follow the on-screen instructions until you reach the Configure extension step. The instructions will show a basic summary of the extension, as well as any resources it will create and access roles it requires.
  5. In the **Cache-Control** header for resized images field, enter the following:

public, max-age=31536000

  1. Leave the other parameters at their default values.
  2. Click Install extension.

While you're waiting for installation to complete...

Installing with the Firebase command-line interface

If you're more comfortable with command-line tools, extensions can be installed and managed using the Firebase CLI, too! Just use the firebase ext command, available in the latest version of the CLI. More information can be found here.

(Optional) Learn more about Cache-Control headers

The Cache-Control header value public, max-age=31536000 means that the image will be cached for up to 1 year. To learn more about the Cache-Control header, check out this documentation.

Update client code

The extension that you installed writes a resized image into the same bucket as the original image. The resized image has the configured dimensions appended to its file name. So, if the original file path looked like friendlymarket/user1234-car.png, the resized image's file path will look like friendlymarket/user1234-car_200x200.png.

Let's update the app so that it fetches the resized images instead of the full-sized images.

  1. In StackBlitz, open the src/firebase-refs.js file.
  2. Replace the existing getImageRef function with the following code to create a ref for the resized image:
export function getImageRef(storage, imagePath) {
  const xDimension = 200;
  const yDimension = 200;

  // find the '.' in 'file.jpg', 'file.png', etc
  const fileExtensionIndex = imagePath.lastIndexOf('.');

  const pathNameWithoutExtension = imagePath.substring(0, fileExtensionIndex);
  const dimensions = `${xDimension}x${yDimension}`;
  const fileExtension = imagePath.substring(fileExtensionIndex);

  return {
    resized: storage().ref(
      `${pathNameWithoutExtension}_${dimensions}${fileExtension}`
    ),
    original: storage().ref(imagePath)
  };
}

Test it out

Since this extension watches for new image uploads, your existing image won't be resized.

Create a new post to see the extension in action:

  1. Click Friendly Market in the top bar of your app to navigate to the home screen.
  2. Click the Sell something button in the bottom-right corner of the app to create a listing.
  3. For Title, enter Coffee
  4. For Asking Price, enter 1
  5. For Item Description, enter the following: Selling one cafe latte. It has foam art in the shape of a bear.
  6. Download this image of a cup of coffee to your computer, and upload it with the PICTURE OF YOUR ITEM button.
  7. After you fill out all the fields and upload an image, click Post. You'll see the coffee listing appear below the Xylophone!
  8. In the Functions dashboard in the Firebase console, click the Logs tab. You should see logs from the function that show that it executed.

486d1226be84bb44.png

  1. Go to the Storage dashboard to see both the original coffee image and a resized version in the friendlymarket path.
  2. In the StackBlitz preview pane, reload your app's home screen a couple of times. You should notice the coffee image loading significantly faster than the xylophone image.

The image loads faster on the first page load because it is smaller, and on subsequent page refreshes it loads from the browser cache instead of triggering a network request.

5. Reconfigure an extension

The problem

Your app auto-saves draft versions of a seller's listing. Your users like this feature, but your stats are a bit worrying. Your reports say that only about 10% of drafts are actually posted, and the other 90% are just taking up space in your database.

The solution

After some back-of-the-envelope calculations, you realize that you only need to save about five drafts at any given time.

Remember that catalog of Firebase Extensions? Maybe there's a solution already built for this situation. Let's install the Limit Child Nodes extension to automatically keep the number of saved drafts at five or fewer. The extension will delete the oldest draft whenever a new draft is added.

  1. Click the Install button on the extension's details page.
  2. Choose the Firebase project that you're using for your marketplace web app.
  3. Follow the on-screen instructions until you reach the Configure extension step.
  4. For Realtime Database path, enter drafts. This is the path in the database where drafts are saved.
  5. For Maximum count of nodes to keep, enter 5. This means that five drafts for each item's listing will be saved, and if another is added, the oldest draft will be automatically deleted.
  6. Click Install extension.

While you're waiting for the installation to complete...

Monitoring extensions

When you install an extension, the process creates several functions. You may want to check how often these functions are running or view logs and error rates. For detailed information on how to monitor your extension, see Manage installed extensions. Follow the directions in the documentation to view the functions created by the Resize Images extension in the previous step.

Uninstalling extensions

To remove an extension from your project, you might be tempted to delete the individual functions that an extension creates, but this can lead to unexpected behavior, since one extension may create multiple functions. Learn how to uninstall an extension in the documentation.

Uninstalling deletes all of the resources (like functions for the extension) and the service account created for that instance of the extension. However, any artifacts created by the extension (like the resized images) will remain in your project after uninstalling the extension.

Installing multiple copies of an extension in a single project

You're not limited to installing a single instance of a given extension in a project. If you wanted to limit entries in another path, you could install another instance of this extension. However, for the purposes of this codelab, you'll install the extension just once.

See it in action

  1. Make sure that you're logged in with the account you used to post the xylophone or latte
  2. Generate some drafts:
  3. Click the Sell something button in the bottom right corner of the app
  4. Edit the Title to say "Draft 1".
  5. Scroll down to the Drafts section and view the number of drafts. There should be at least two.
  6. Click the FRIENDLY MARKET button in the top app bar. This way, you'll have a saved draft but don't need to post it.
  7. Repeat for "Draft 2", "Draft 3", and so on to "Draft 6".
  8. When you create "Draft 6", notice that "Draft 1 disappears from your Drafts section.
  9. As you did with the Resize Images extension, you can check the functions logs to see what functions triggered.

Oops, the limit of drafts to keep is too small

Your customer support team reaches out and lets you know that some of your most prolific sellers are complaining that their drafts are being deleted before they can post them. You check your math with your teammate, and you realize that your math was off by a factor of 10,000!

How can you fix this? Let's reconfigure the installed extension!

  1. In the left pane of the Firebase console, click Extensions.
  2. On the installed extension's card, click Manage.
  3. In the upper-right corner, click Reconfigure extension.
  4. Change Maximum count of nodes to keep to 50000.
  5. Click Save.

And that's all that you need to do! In the time the extension takes to update, you can talk to your support team and let them know that a fix is already being deployed.

6. Automatically delete user data

The problem

Your customer support team has contacted you again. Sellers who deleted their accounts are still getting emails from other users, and they're angry! These sellers expected that their email addresses would be deleted from your systems when they deleted their accounts.

For now, support has been manually deleting each user's data, but there has to be a better way! You think about it, and you consider writing your own batch job that runs periodically and clears out email addresses from deleted accounts. But deleting user data seems like a pretty common problem. Maybe Firebase Extensions can help solve this problem, too.

The solution

You'll configure the Delete User Data extension to automatically delete the users/uid node in the database when a user deletes their account.

  1. Click the Install button on the extension's details page.
  2. Choose the Firebase project that you're using for your marketplace web app.
  3. Follow the on-screen instructions until you reach the Configure extension step.
  4. For Realtime Database paths, enter sellers/{UID}. The sellers part is the node whose children contain user email addresses, and {UID} is a wildcard. With this configuration, the extension will know that when the user with the UID of 1234 deletes their account, the extension should delete sellers/1234 from the database.
  5. Click Install extension.

While you're waiting for the installation to complete...

Let's talk about customizability

In this codelab, you've seen that Firebase Extensions can help solve common use cases and that extensions are configurable to fit your app's needs.

However, extensions can't solve every problem, and the issue of user data deletion is a good example of that. Though the Delete User Data extension addresses the current complaint that emails are still exposed after a user deletes their account, the extension won't delete everything. For example, the item listing is still available, and any images in Cloud Storage will stick around, too. The Delete User Data extension does allow us to configure a Cloud Storage path to delete, but since users can upload many different files with lots of different names, you won't be able to configure this extension to automatically delete these artifacts. For situations like this, Cloud Functions for Firebase might be a better fit so that you can write code that is specific to your app's data model.

Extensions and billing

Firebase extensions themselves are no-cost to use (you're only charged for the underlying resources that you use), but some of the underlying resources needed by an extension may require billing. This codelab was designed to be completed without a billing account. However, setting up a Flame or Blaze plan unlocks a lot of really interesting Firebase extensions.

For example, you can shorten URLs, trigger email, export collections to BigQuery, and more! Check out the full catalog of extensions here.

If there's an extension that you'd like to have, but it isn't available right now, we'd love to hear about it! File a feature request with Firebase Support to suggest a new extension.

See it in action

After the installation of your extension is complete, delete a user and see what happens:

  1. In the Firebase console, head over to your Realtime Database dashboard.
  2. Expand the sellers node.
  3. Each seller's information is keyed on their user UID. Pick a user's UID.
  4. In the Firebase console, head over to your Authentication dashboard, and find that user UID.
  5. Expand the menu to the right of the UID, and select Delete Account.

2e03923c9d7f1f29.png

  1. Go back to your Realtime Database dashboard. The seller's information will be gone!

7. Congratulations!

Even though you didn't write much code in this codelab, you added important features to your marketplace app.

You learned how to discover, configure, install, and reconfigure extensions. In addition, you learned about how to monitor installed extensions and how to uninstall them, if necessary.

What's next?

Check out some of these other extensions:

Need more custom server-side code?

Other helpful documents

Managing extensions:

Learning the finer details about extensions: