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.
In this codelab, you'll add the following features to an online marketplace web app:
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.
The application that you'll build uses a few Firebase products available for web apps:
You'll now enable and configure those Firebase products, using the Firebase console.
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.
The app uses the Firebase Realtime Database to save items for sale.
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.
{
"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"
}
}
}
The app uses Cloud Storage for Firebase to save images of items for sale.
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.
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;
}
}
}
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.
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.
src/firebase-config.js
file. This is where you'll add the Firebase configuration object.<script>
tags. Note: If you need to find the configuration later, follow the instructions here.Add your project's configuration to your app:
src/firebase-config.js
file.Take a look at the interactive preview on the right side of the StackBlitz screen:
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:
Xylophone
.50
.This high quality xylophone can be used to play music.
forsale
node. In the Storage dashboard, you'll also find the image you uploaded in the friendlymarket
path.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!
After reading up on how to optimize images, you decide to take two steps to improve the image loading performance:
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:
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?
You're in luck. There is an easier solution: Firebase Extensions! Let's check the catalog of available extensions on the Firebase website.
Look at that! There's an extension called "Resize Images". That looks promising.
Let's use this extension in your app!
Cache-Control
header for resized images field, enter the following:public, max-age=31536000
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.
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.
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.
src/firebase-refs.js
file.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)
};
}
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:
Coffee
1
Selling one cafe latte. It has foam art in the shape of a bear
.friendlymarket
path.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.
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.
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.
drafts
. This is the path in the database where drafts are saved.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.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.
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.
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.
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!
50000
.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.
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.
You'll configure the Delete User Data extension to automatically delete the users/uid
node in the database when a user deletes their account.
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.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.
Firebase extensions themselves are free 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.
After the installation of your extension is complete, delete a user and see what happens:
sellers
node.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.
Check out some of these other extensions:
Managing extensions:
Learning the finer details about extensions: