# Introduction

## Learn Production-Worthy Firebase

{% embed url="<https://youtu.be/fQosnpJ4x-Q>" %}
Full-Stack Firebase on Udemy
{% endembed %}

## Available on Udemy

![Click the link below to learn more 👇](https://923086279-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDCGLGoCrsYuj1f3a2-%2F-LH3jlo57yX_zY7WWGxF%2F-LH3kE8RW_ytfdGYuQ11%2Fhowtofirebase%252F640%252Fudemy-hero%20\(1\).png?alt=media\&token=1e727e15-211a-4a1e-bed5-77e70f57e1f6)

We've launched [Full-Stack Firebase on Udemy](https://www.udemy.com/full-stack-firebase/?couponCode=FSFDOTCOM) for the full video experience.

The Udemy course features 2.5 hours of tightly-edited video walkthroughs. It is the absolute fastest, easiest way to learn Firebase. We've condensed everything on FullStackFirebase.com into video form with free instructor Q\&A.

## Newsletter

[Sign up for email updates](http://eepurl.com/ceGkov) 💌

We'll send you some "getting started" emails. From there on out, we email with a light touch 👍

## GitBook vs GitHub

Read the GitBook or the GitHub repo:

* [GitBook](https://www.fullstackfirebase.com/): A luxurious reading experience
* [GitHub](https://github.com/how-to-firebase/full-stack-firebase): The delightful and gory details

## Beginners Welcome!

You'll need to know JavaScript. If you're weak on JavaScript... this may not be the course for you.

Check out the Prerequisites chapter for all of the details.

You don't need to know anything about Firebase to get started. Just be patient and move slowly through the material.

If you're already adept with Firebase, then please jump around! The more advanced modules later in the course will be of particular interest.

We'll cover integration for Angular and React. The Firebase SDK is all vanilla JavaScript, so if you can integrate with Angular and React, you should be able to integrate with any other modern framework.

## Modules

This course consists of a bunch of modules, including:

* Firebase Console
* Firebase Authentication
* Cloud Firestore
* Realtime Database
* Cloud Functions
* Firebase Storage
* Cloud Messaging
* Firebase Hosting

## Module Structure

Each module will have the following parts:

* Introduction
* Feature walk-through(s)
* Exercise(s)
* Downloadable notes

Some modules will build on each other, so skip around carefully 😉

## What to expect

* Opinionated code
* Angular and React integrations
* You'll need a Firebase account

We write opinionated code, and we like our courses opinionated as well.

If you're not familiar with Angular or React, do not worry. The markup and JavaScript is easy enough to read, and we'll stick to vanilla JS as much as possible.

Firebase's [free Spark plan](https://firebase.google.com/pricing/) is **very generous**. Firebase Functions requires a paid Blaze plan ***IF*** you want to make external HTTP calls. Otherwise, you can skate by on Spark.

## This course is not...

* 100% framework agnostic
* Exhaustive documentation
* Constantly updated

Firebase is framework-agnostic... but good luck writing a modern web application without some sort of templating framework.

We'll reference the [Firebase Docs](https://firebase.google.com/docs/) quite a bit throughout. We will try to duplicate them.

Firebase changes. A lot. The docs are canonical. Use them. We'll do our best to update this content, but staying 100% current would be a full-time job.

Have you found an error or a bug? Don't hesitate to [file an issue ](https://github.com/how-to-firebase/full-stack-firebase/issues) or make pull requests 💕


# Full-Stack Firebase

## Learn Production-Worthy Firebase

{% embed url="<https://youtu.be/fQosnpJ4x-Q>" %}
Full-Stack Firebase on Udemy
{% endembed %}

## Available on Udemy

![Click the link below to learn more 👇](https://923086279-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDCGLGoCrsYuj1f3a2-%2F-LH3jlo57yX_zY7WWGxF%2F-LH3kE8RW_ytfdGYuQ11%2Fhowtofirebase%252F640%252Fudemy-hero%20\(1\).png?alt=media\&token=1e727e15-211a-4a1e-bed5-77e70f57e1f6)

We've launched [Full-Stack Firebase on Udemy](https://www.udemy.com/full-stack-firebase/?couponCode=FULLSTACK2018) for the full video experience.

The Udemy course features 2.5 hours of tightly-edited video walkthroughs. It is the absolute fastest, easiest way to learn Firebase. We've condensed everything on FullStackFirebase.com into video form with free instructor Q\&A.

## Newsletter

![Click below 👇 to subscribe](https://923086279-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDCGLGoCrsYuj1f3a2-%2F-LH3kKXj3NU2mPmh_pOM%2F-LH3kfWalQoNgdTbWZAd%2Femail-signup-form.png?alt=media\&token=a076ea36-9c45-410d-ab0b-22518a847cb3)

[Sign up for email updates](http://eepurl.com/ceGkov) 💌

We'll send you some "getting started" emails. From there on out, we email with a light touch 👍

## GitBook vs GitHub

Read the GitBook or the GitHub repo:

* [GitBook](https://www.fullstackfirebase.com/): A luxurious reading experience
* [GitHub](https://github.com/how-to-firebase/full-stack-firebase): The delightful and gory details

## Beginners Welcome!

You'll need to know JavaScript. If you're weak on JavaScript... this may not be the course for you.

Check out the Prerequisites chapter for all of the details.

You don't need to know anything about Firebase to get started. Just be patient and move slowly through the material.

If you're already adept with Firebase, then please jump around! The more advanced modules later in the course will be of particular interest.

We'll cover integration for Angular and React. The Firebase SDK is all vanilla JavaScript, so if you can integrate with Angular and React, you should be able to integrate with any other modern framework.

## Modules

This course consists of a bunch of modules, including:

* Firebase Console
* Firebase Authentication
* Cloud Firestore
* Realtime Database
* Cloud Functions
* Firebase Storage
* Cloud Messaging
* Firebase Hosting

## Module Structure

Each module will have the following parts:

* Introduction
* Feature walk-through(s)
* Exercise(s)
* Downloadable notes

Some modules will build on each other, so skip around carefully 😉

## What to expect

* Opinionated code
* Angular and React integrations
* You'll need a Firebase account

We write opinionated code, and we like our courses opinionated as well.

If you're not familiar with Angular or React, do not worry. The markup and JavaScript is easy enough to read, and we'll stick to vanilla JS as much as possible.

Firebase's [free Spark plan](https://firebase.google.com/pricing/) is **very generous**. Firebase Functions requires a paid Blaze plan ***IF*** you want to make external HTTP calls. Otherwise, you can skate by on Spark.

## This course is not...

* 100% framework agnostic
* Exhaustive documentation
* Constantly updated

Firebase is framework-agnostic... but good luck writing a modern web application without some sort of templating framework.

We'll reference the [Firebase Docs](https://firebase.google.com/docs/) quite a bit throughout. We will try to duplicate them.

Firebase changes. A lot. The docs are canonical. Use them. We'll do our best to update this content, but staying 100% current would be a full-time job.

Have you found an error or a bug? Don't hesitate to [file an issue ](https://github.com/how-to-firebase/full-stack-firebase/issues) or make pull requests 💕


# Why Firebase?

Firebase is an application platform for web, Android and iOS.

We know web, not Android or iOS, and we're teaching what we know; therefore, this course will be entirely focused on Firebase for web.

So why should you use Firebase as your web app platform?

## Everything you need, integrated

You want to use Firebase because it gives you a huge suite of services that you don't have to write.

You could duplicate Firebase's services yourself, but why spend the time and money? And good luck coming in under Firebase's price point. It's not like Firebase is some extravagantly expensive service. The billing structure is entirely reasonable and one of the least expensive options... especially when you consider developer time saved.

And all of Firebase's services are fully integrated. They talk to each other out of the box and use cohesive APIs. So you won't waste time architecting. You won't waste time integrating micro-services. You'll cough up a few pennies for Google's compute and storage resources, and you'll spend your valuable time writing your own application logic.

In short, Firebase saves you time. We've seen greater-than 50% reductions in developer workload for even simple projects. Your codebase stays smaller. Your bugs stay fewer. And you can ship more features with the same team.

And by saving you time, Firebase saves you money. Lots of money.

## Production-worthy infrastructure

Startups need to write minimum-viable products. They need to prototype. They don't need to focus on scaling in the early days.

Firebase lets you bootstrap your initial prototype on Google-scale infrastructure. This is a massive selling point.

Consider the typical early-product lifecycle.

You build a prototype. Customers like it, so you expand it to a minimum-viable product and sell it to the marketplace.

The marketplace likes it, so now you have to manage a growing user-base AND ship new features.

A typical startup architecture would include servers and databases, all of which you'd need to provision and scale to keep up. This leaves your team strapped for time, because they're still building new features.

Firebase architecture abstracts all of that away. Your code is "serverless", meaning that you don't manage servers yourself. You write code and Firebase puts it on Google's servers and scales it up and down as needed.

You can service millions of customers on the same architecture and infrastructure that you used for your prototype.


# What is serverless?

Firebase enables your team to use a "serverless" architecture. Serverless is a horribly misleading term, because it involves servers. Lots of servers.

To understand serverless, let's start with the original application architecture: the monolith.

## Monoliths can be great... for some things

For years application have been developed as monoliths. A monolith is a single program that runs your entire website. Think Microsoft Excel. You install a single .exe file, and your computer uses Excel.exe to open and run .xslx files.

Web applications started off as monoliths. Your company would develop a single application, often written in .Net, Java or PHP, and run it on one server. When that server would get overloaded, they'd spin up a second server running a second copy of the application. And so on until hundreds of servers were running hundreds of applications.

Monoliths are often the easiest way to write an application, especially in the early days of a product; however, they can be truly evil to scale. So as an application grows, companies often migrate to a microservices architecture.

## Microservices scale differently

[Microservices](https://martinfowler.com/articles/microservices.html) are usually the next step after a company's monolith is overrun.

Microservices use HTTP calls to connect tens or hundreds of tiny apps together.

So instead of a single monolith that handles authentication, payments and sending emails to clients, a microservices architecture has one app for authentication, another for payments and another for filling your inbox with marketing emails.

Microservices allow us to scale up just the parts of our application that need it. So a single, tiny server may handle authentication, while ten servers send emails. You get to allocate resource where they're needed most, and the application architecture scales much better.

But you still have to manage infrastructure. You still have to spin up ten email servers, and you still need to make sure that those email servers stay in sync as customer data changes. It's manageable, but is there a better way??? Well... guess what! There ***sometimes*** is a better way!

## Serverless

The next progression in the app-fragmentation process is known as serverless. There are still plenty of servers, but you're not managing them any longer! Google, Amazon or Microsoft now get to manage the servers, and you merely manage your own code.

Serverless is much more difficult from a service provider's point of view. The Google engineers who make your code run as if by magic are using extremely sophisticated tooling and programming methods to automatically scale your apps up and down.

The upside of serverless is that it saves the user—in this case you—significant time and resources. You deploy code to your service provider, and the service provider runs it and scales it up and down.

## Serverless is a collection of technologies

Most "serverless" web apps use a combination of technologies. These technologies include

* single-page applications (SPAs) written in JavaScript that provide the graphical user-interface to your users,
* a static file host to serve the JavaScript, CSS and HTML files necessary for the SPA,
* a cloud-based database that the SPA connects to directly, and finally,
* a functions-as-a-service provider that runs secure functions on a server.

## Single-page applications

Single-page applications, also known as SPAs, are browser-based applications that render their own HTML and CSS. In olden times these pages were all rendered on the server. While server rendering is still the fastest way to get a page, especially for static content like blogs and news articles, most sophisticated web applications are now single-page applications.

It's a single-page application because the server renders just one page, and once the browser has that page, the JavaScript on the page manages all future renders. This takes the load off of the server and moves it onto the user's browser. Having the users do your processing is awesome. The load is distributed. Also, your users can get instant feedback as they make changes to the page.

Single-page applications are usually hosted on a static file host. Firebase Hosting is a static file host, and it's tuned just for that. It doesn't do any rendering of files. It doesn't serve dynamic content. It serves static JavaScript, HTML, images... whatever your single-page app needs to run.

## Functions-as-a-service

Functions-as-a-service is the next building block in our serverless architecture.

Single-page applications can do a lot of processing, but there are things that you ***NEVER*** want to do in a browser. For instance, you should never attempt to send an email in a browser. You shouldn't attempt to finalize a Stripe payment either.

While these things could potentially be done in a browser, these operations require secret API keys. For instance, finalizing a Stripe payment requires your secret Stripe API keys. If you were to send those keys to a browser it would be trivial for any user to inspect the network request and use your API keys to take over your account.

Secure operations must be done on a secure server. But this architecture is called serverless! How on Earth does this work???

The answer is functions-as-a-service, or FaaS. Google's functions-as-a-service is called Cloud Functions. You may have heard of Amazon's FaaS offering, AWS Lambda.

This is a Firebase course, so naturally, we're using Cloud Functions.

Cloud Functions enables the developer to write discrete pieces of logic, known as functions, and export them to the Cloud Functions service for execution. Cloud Functions then listens to any one of a number of triggers. When these triggers are tripped, Cloud Functions executes your code with the trigger as the event context.

We'll get deep, deep into Cloud Functions in a future module. Just know that these triggers can be as standard as an HTTP call directly to the Cloud Function, or as Firebase-specific as a new Firebase Authentication sign up or a change in your Cloud Firestore database.

The point is that by using triggers and Cloud Functions we can architect an app that scales from prototype to massive success without us lifting a finger.


# Prerequisites

There are always prerequisites.

## JavaScript all the way down

We'll be doing the bulk of our work in JavaScript. Firebase supports JavaScript for the web, Java for Android and whatever language iOS uses these days. It also has quite a bit of support for C++, Go and Python.

We focus entirely on JavaScript. We only write JavaScript. We're web guys, and this is a web course, so we won't get distracted with any non-JavaScript languages.

## HTML & CSS

Unfortunately for us all, the web browser is still an under-developed platform. It needs some sugar to make it digestible, and by sugar we're referring to frameworks.

We're huge fans of native web components and hope that they bring an end to the JavaScript framework wars; however, we live in a world with Internet Explorer 11 and old Firefox browsers, so first-class web component support is still a few years away for most of us.

Therefore we're using frameworks instead of developing in native web components. We'll stick to the two most popular and universal frameworks, Angular 2+ and React. Angular 2+ has massive adoption, and so does React. Additionally, React has spawned an entire ecosystem of spinoff frameworks such as Preact and Inferno which use JSX for rendering. If you're comfortable with JSX, you'll have no trouble following our React code.

## Firebase is vanilla

The bright side is that after agonizing over framework decisions and just how we plan to render our browser pages, the Firebase SDK is entirely vanilla.

And by vanilla we mean vanilla JavaScript. It's just JavaScript. You can copy/paste our code samples into Vue.js or Stenciljs or jQuery for that matter, and they will just work.

The Firebase SDKs also aim for parity between Node.js and the browser. Don't get us wrong... the underlying code is quite different in Node.js and the browser environment; however, the Firebase SDK provides a very similar ***external*** API in both environments, so your life just got much simpler. The Firebase calls that you make in the browser will look nearly identical to the calls that you'll make in Node.js.

## And speaking of Node.js...

Cloud Functions, Google's functions-as-a-service or FaaS platform, uses Node.js. It also uses the command line.

If you're still not comfortable with the command line, don't worry. We'll take it slowly. However! Node.js is executed on a server, not in a browser. So we'll be executing Node.js locally in our test environment before shipping it off to Cloud Functions to be run in Cloud Functions' functions-as-a-service environment.

Also, some interactions with Firebase services require the *Firebase Tools*ah command-line interface or CLI. Firebase Tools is not particularly complicated... but it does use the command line.

Every developer needs to get comfortable with the command line at some point. Today is a golden opportunity!


# Overview & Structure

This course consists of a bunch of modules, including:

* Firebase Console
* Firebase Authentication
* Firestore
* Realtime Database
* Cloud Functions for Firebase
* Firebase Storage
* Cloud Messaging
* Firebase Hosting

## Modules

Some of these modules are **much** larger than others.

For instance, we'll breeze through Authentication, Storage, Hosting and Messaging. These topics are isolated and straightforward. They don't require much decision-making. We need to get comfortable with how these services fit into a production-worthy Firebase architecture, and then we can move on.

However, Cloud Firestore, the Realtime Database, Security Rules and Cloud Functions are an entirely different story.

These modules are multi-purpose tools that can be used in all sorts of creative ways. We'll go much deeper into these Firebase features. We'll cover common use cases and potential pitfalls. And we'll try to inspire you with advanced architectures that will make your apps both performant and easier to write.

| Module         | Intensity  | Reasoning                                                                            |
| -------------- | ---------- | ------------------------------------------------------------------------------------ |
| Console        | 🌶         | A quick walk-through                                                                 |
| Authentication | 🌶         | Auth is the easiest Firebase feature to implement.                                   |
| Firestore      | 🌶🌶🌶🌶🌶 | Firestore is the meat of Firebase's offering.                                        |
| Realtime DB    | 🌶🌶🌶🌶   | The Realtime DB (RTDB) has some "gotchas"                                            |
| Functions      | 🌶🌶🌶🌶🌶 | Functions is the back end of your app. It's tricky to develop on, but crazy powerful |
| Storage        | 🌶🌶       | Small API with some complexity                                                       |
| Messaging      | 🌶🌶       | The Firebase SDK hides most complexity                                               |
| Hosting        | 🌶🌶       | Static file hosting has some detail, but it's mostly just static files.              |

## Module Structure

Each module will start of with an introduction. Introductions answer questions like "why do you need Firebase Authentication?" or "how does Cloud Functions work with Firestore?"

Next, we'll walk through how the feature is used in a live application. This is a public-facing web app whose code you can inspect and interact with yourself. You can also pull down the code and deploy it to your own Firebase project for a deeper dive into its features.

Once we've seen the Firebase service in action, we'll spin up our own Firebase project and implement a feature step by step.

Finally, we'll summarize what we've learned, provide you with some notes--kind of a cheatsheet for the feature--and refer you to additional learning resources and next steps.

## Example Module

Let's use Firebase Authentication as an example.

First, we'll explain why Firebase Authentication is critical to your application. Why you need it, and how your app will be better because you took the time to implement it.

Next, we'll show how Firebase Authentication works in our production app.

Then we'll break out our code editors and implement a basic authentication. We won't go any deeper than we need to, but you'll have coded a working implementation that you can take with you.

And finally we'll wrap up with a summary of how we've implemented Firebase Authentication, downloadable notes with descriptions of the functions that you should know, and suggestions for next steps in implementing Firebase Authentication in a more sophisticated way.


# Write your first query

We'll be using Firestore. Just follow the instructions below.

### Instructions

1. Open up [the Firestore docs](https://firebase.google.com/docs/firestore/query-data/get-data) in a new browser tab.
2. Scan the docs, especially the "Get a document" section.
3. Open another tab and go to Glitch.com.
4. Create a Glitch.com account and click around a bit to get comfortable.
5. Visit the Glitch project for this challenge, [coordinated-freighter](https://glitch.com/edit/#!/coordinated-freighter)
6. Look over the code in `index.html` and read the comments.
7. Try to query the `star-wars-people` collection inside the `getPeople()` function

## Check out your results

1. Click the "Show" button on your glitch to pop open a preview of your page.
2. [Open Chrome's DevTools](https://developer.chrome.com/devtools).
3. Navigate to the [Console](https://developers.google.com/web/tools/chrome-devtools/console/?utm_source=dcc\&utm_medium=redirect\&utm_campaign=2016q3) tab in DevTools to view your JavaScript output

### If you get stuck...

1. Reference [the Firestore docs](https://firebase.google.com/docs/firestore/query-data/get-data) as necessary
2. Check out the solution in `complete.html` and watch the solution video!


# Links

Read the online book at [FullStackFirebase.com](https://www.fullstackfirebase.com/)

Check out our [Glitch.com demo library](https://glitch.com/@deltaepsilon)

Our [How To Firebase GitHub Org](https://github.com/how-to-firebase) hosts all of our code:

* [full-stack-firebase](https://github.com/how-to-firebase/full-stack-firebase)
* [firelist-react](https://github.com/how-to-firebase/firelist-react)
* [firelist-angular](https://github.com/how-to-firebase/firelist-angular)
* [tutorials](https://github.com/how-to-firebase/tutorials)
* [fogo](https://github.com/how-to-firebase/fogo)

## Introduction

[Write your first query](https://glitch.com/edit/#!/coordinated-freighter)

## Firebase Console

[Browser configuration](https://glitch.com/edit/#!/malachite-engine)

[Node.js configuration](https://glitch.com/edit/#!/thread-asphalt)

## Firebase Tools

[Firebase Tools](https://glitch.com/edit/#!/somber-binder)

## Firebase Authentication

[Fogo](https://fogo.howtofirebase.com/login)

[Authentication Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/firebase-authentication/notes.pdf)

## Cloud Firestore

[Where and orderBy](https://glitch.com/edit/#!/earthy-rhinoceros)

[Firestore Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/cloud-firestore/notes.pdf)

## Realtime Database

[Firebase Chat](https://glitch.com/edit/#!/truth-spleen)

[Realtime Database Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/realtime-database/notes.pdf)

## Cloud Functions for Firebase

[Firebase Chat with Functions](https://glitch.com/edit/#!/merciful-quicksand)

[Cloud Functions Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/cloud-functions-for-firebase/notes.pdf)

## Firebase Storage

[Fogo](https://fogo.howtofirebase.com/login)

[Storage Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/firebase-storage/notes.pdf)

## Cloud Messaging

[Firebase Cloud Messaging walkthrough](https://glitch.com/edit/#!/fine-ping)

[Cloud Messaging Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/cloud-messaging/notes.pdf)

## Firebase Hosting

[Firebase Hosting walkthrough](https://glitch.com/edit/#!/ripe-knife?path=firebase.json:3:23)

[Hosting Notes](https://github.com/how-to-firebase/full-stack-firebase/raw/master/firebase-hosting/notes.pdf)

## Next Steps

[Firebase Community Slack](https://firebase.community/)

[@ChrisEsplin (Twitter)](https://twitter.com/chrisesplin)

[@JuarezPAF (Twitter)](https://twitter.com/juarezpaf)


# Downloadable Notes

![Notes Example](https://923086279-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDCGLGoCrsYuj1f3a2-%2F-LH2zWr_yuRslUj-wAmf%2F-LH2zbaaXRRwOMx0Pkx1%2Fimage.png?alt=media\&token=cfa126f8-71a6-4bd2-a541-3317a7defccd)

* [All PDFs Zipped](https://github.com/how-to-firebase/full-stack-firebase/raw/master/notes.zip)
* [All notes in a single, combined PDF](https://github.com/how-to-firebase/full-stack-firebase/raw/master/notes/full-stack-firebase-notes-combined.pdf)
* [Individual note files](https://github.com/how-to-firebase/full-stack-firebase/tree/master/notes)


# Introduction

We'll kick this party off with a quick run down of the Firebase Console.

It's how you'll interact with most of the Firebase features. You'll need your Console to configure your Firebase services and watch data flow through your project.

Most of the Console's functions are also accessible via the Firebase Tools command-line interface, or CLI; however, we always have our Firebase Console open in a browser tab whenever we're developing on Firebase. So make good friends with your Firebase Console and it will help carry your app to serverless glory.


# Walkthrough

We'll kick this party off with a quick run down of the Firebase Console.

It's how you'll interact with many of the Firebase features.

You'll need your Dashboard to configure your services and watch data flow through your project. Most of the Dashboard's functions are also accessible via the Firebase Tools command-line interface.

Command-line interface is a mouthful, so we'll refer to it as a CLI.

## firebase.google.com

So let's log in to Firebase. Visit [firebase.google.com](https://firebase.google.com/) and click the SIGN IN button

You'll sign in with your Google account and then land on your project selection screen.

Click "Add project" or select a project that you've already created.

![Add A Project](https://goo.gl/HjLmzL)

Once your project is live you'll be taken to that project's overview.

The project overview has one button that we use, "Add Firebase to your web app".

You can copy/paste values from this popup directly into your 'index dot html'.

![Add Firebase to your web app](https://goo.gl/LCK1fW)

Now notice the gears button. We won't spend much time here, but it's good to click around. Notice that you can change your project name and generate service accounts. We'll use the service accounts later when we're using Node.js in admin mode.

But let's continue on to the left bar.

![Project Overview](https://goo.gl/wMS5Qq)

The left bar has four sections, Develop, Stability, Analytics and Grow. The first section, Develop, is the one that we use for web. The other sections are mobile only.

Google rolled a bunch of their mobile platform products into the Firebase brand; however, Google's web products, such as Analytics and AdSense, are not under the Firebase brand

So let's start with Authentication.

## Firebase Authentication

Our user accounts list is empty, because we haven't enabled any sign-in methods, so let's do that. We have a variety of sign-in methods.

![Sign-In Methods](https://goo.gl/4GFwXV)

Our favorites are Email/Password and Google. So let's enable those methods. Next we'll need to make sure that our domains are authorized, especially for development.

Later in the course we'll be coding on Glitch.com. So let's authorize the Glitch.me domain. Glitch serves its pages up on Glitch.me, not on Glitch.com.

![Authorized Domains](https://goo.gl/K4o6Wn)

Let's also allow creation of multiple accounts per email address. This will automatically merge duplicate accounts created under the same email address. It's common for users to create an email/password account with a GMail address and later try to log in with Google using that same GMail address.

Now let's click through the Templates and Usage tabs.

![Authentication Templates](https://goo.gl/cqbxgr)

Firebase will send some email on your behalf, so it's nice to customize those emails.

And you'll have to pay for phone authentication if your app gets enormous. Having a hugely successful app would be a great problem to have. We hope to someday suffer that plight and spend more time monitoring usage.

But until then... let's forge ahead to Firebase's juicy core, the databases.

The Database section will give you two options, the Realtime Database or Cloud Firestore. The Realtime Database has tabs for Data, Rules, Backups and Usage. We can use the Data tab to view and modify the data in our JSON tree.

![Realtime Database Data](https://goo.gl/sPVPcK)

The Rules tab is great for visualizing and testing security rules. You can use the security rules simulator to simulate reads and writes. This is useful for making sure that your data is locked down and secure.

Moving on... the Backups tab is great once you're in production. You'll want to enable frequent database backups. They're cheap insurance against data loss.

And the Usage tab will, as we've seen before, help you manage your costs as you scale.

Let's continue on to the Storage section.

## Firebase Storage

![Firebase Storage](https://goo.gl/vzTRMG)

It's pretty spare with only two tabs, Files and Rules. We can use the File browser to create folders and upload files. Once we have files in our Storage bucket we can also view file details and some metadata. We'll cover security rules for Storage in a later chapter.

So let's continue on to Hosting!

![Firebase Hosting](https://goo.gl/Jy9jDp)

You'll mostly use Hosting's Dashboard tab to connect a custom domain. Firebase Hosting provides a \*.firebaseapp.com subdomain for each project. But when you're ready for real users, you'll want a custom domain with free SSL certs. You can also manage your deploys and review your deployment history.

The Usage tab is, again, most useful for cost control if your site gets popular.

So on to Functions.

## Firebase Functions

![Firebase Functions](https://goo.gl/SW1R2d)

Cloud Functions for Firebase is the "serverless" glue that holds your apps together. There's nothing to see here now, but we'll get very familiar with both the Dashboard and Logs tabs.

Cloud Functions are deployed via Firebase's CLI. We'll use the terminal to deploy our Functions, but we'll monitor the deploys here. We'll also spend time on the Logs and Usage tabs to make sure our Functions behave themselves.

And finally, let's click the Modify pricing plans button to see our options.

## Pricing

![Firebase Pricing](https://goo.gl/M1sroi)

Blaze includes the same free usage as Spark, but you'll pay as you exceed those limits.

All of our development usage and some of our production usage falls within the free limits.

Flame is never cheaper than Blaze... it's just price capped at 25 US dollars

We use Blaze for all of our projects.

## Conclusion

And that's the Firebase Console!

We'll dive deeper into each console section as we learn the associated Firebase features.

There's a lot to learn here, but you'll need a rich, active project for it to get interesting


# Introduction

The Firebase Console handles most administrative functions, but it doesn't deploy code, and there are some admin functions that don't have console dashboards yet.

That's where Firebase Tools comes in.

Firebase Tools is a command-line interface or CLI that uses Node.js to administer your Firebase projects. It has a ton of capabilities, but we'll stick to the basics.


# Walkthrough

You'll get started with Firebase Tools by installing it. Open up your terminal and make sure you have Node.js installed:

```bash
$ node --version 
$ #some version number
```

If you see `bash: command not found: node`, then you'll need to [install Node.js](https://nodejs.org/en/download/).

Now it's time to install Firebase Tools.

```bash
$ npm -g install firebase-tools
```

To confirm successful installation:

```bash
$ firebase --version 
$ #some version number
```

To get help:

```bash
$ firebase --help
```

## Initialize your app

We mostly use Firebase Tools to initialize our apps. Just make a new folder, `cd` into it, and call `firebase init`.

```bash
mkdir my-new-project
cd my-new-project
firebase init
```

Select all of the Firebase features. Because why not!

![firebase-init.png](https://goo.gl/Co7fU9)

Now follow the prompts, sticking to the defaults with one exception:

```bash
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y
```

It's easy to remove the single-page app rewrites later if you don't need them. Your should `firebase.json` file should look something like this:

```javascript
{
  "database": {
    "rules": "database.rules.json"
  },
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "storage": {
    "rules": "storage.rules"
  }
}
```

## Deploy

Run `firebase deploy` to deploy your new project.

You may get an error like...

```
Error: HTTP Error: 400, Project 'how-to-firebase-tutorials' is not a Firestore enabled project.
```

... in which case, you'll need to use your Console to activate the Firestore Beta. Make sure to start in locked mode.

![enable-firestore.png](https://goo.gl/4oX1a8)

![locked-mode.png](https://goo.gl/6MSy65)

Now try `firebase deploy` again if necessary.

Notice the final line of output that looks like this:

```
Hosting URL: https://how-to-firebase-tutorials.firebaseapp.com
```

Follow that url to see the results!

## Deploy pipeline

The Node.js community has moved toward putting all deploy commands in a `package.json` file, so let's initialize our project for Node.js as well and set up our deploy pipeline.

### Note

> You likely have `package.json` and other `npm` files under `./functions/` because you ran `firebase init` and initialized the functions. The following steps will add new `npm`-related files in your root directory (`./package.json` and such). This is intended, because both folders will act as separate NPM packages.

```bash
$ npm init #go ahead and accept the defaults
```

We've already installed Firebase Tools globally with `npm -g install firebase tools`. Now that we have a local `package.json` file, let's install it locally as well.

```bash
npm install --save-dev firebase-tools
```

Now that we have a local version of `firebase-tools` our `package.json` scripts will run using the local version instead of the global version. This is super helpful if you copy your code to a machine that doesn't have Firebase Tools installed globally.

Now let's add some scripts:

```javascript
{
  "name": "starter-project",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "firebase-tools": "^3.17.4"
  },
  "scripts": {
    "deploy": "firebase deploy",
    "deploy:hosting": "firebase deploy --only hosting",
    "deploy:database": "firebase deploy --only database",
    "deploy:firestore": "firebase deploy --only firestore",
    "deploy:storage": "firebase deploy --only storage",
    "deploy:functions": "firebase deploy --only functions"
  }
}
```

We've added a `scripts` attribute and created scripts for each kind of deploy that we could use. We won't need to call `firebase deploy` directly anymore. Instead we'll call `npm run deploy` or `npm run deploy:hosting`.

This is a great habit to promote. It both documents and standardizes your deploy pipeline so that you can come back to this project in two years and know exactly how to deploy it.

Try running `npm run deploy:hosting` to test it out!

## Firebase deploy

Firebase Tools deploys five different modules, each of which can be deployed individually with the `--only` flag:

* hosting
* database
* firestore
* storage
* functions

### Hosting

Hosting deploys your public folder to Firebase Hosting. It also deploys any hosting settings you have in `firebase.json`.

### Database

The database module deploys your security rules to the Realtime Database as defined in `database.rules.json`.

### Firestore

The firestore module deploys two files, `firestore.rules` and `firestore.indexes.json`. These files contain security rules and index specifications.

### Storage

The storage module deploys the security rules defined in `storage.rules`, which you'll use to secure Firebase Storage.

### Functions

The functions module is the trickiest of the bunch. It runs a bunch of checks on your `/functions` folder to make sure that all of the right NPM packages are installed and that `/functions/index.js` exports valid Cloud Functions.

Firebase Tools will deploy your functions to Cloud Functions once its validation checks pass. But beware! Firebase Tools can't validate your code very deeply. It just makes sure that you're importing modules correctly and attaching functions to valid endpoints.

We ***HIGHLY*** recommend--in all caps no less--developing Cloud Functions in a local testing environment. Don't get sucked into the tempting feedback loop of deploying a function, testing it on the Cloud Functions servers, making edits and deploying again... This is your ticket to Cloud Functions hell 😈

We'll cover Cloud Functions test-driven development elsewhere. You have been warned! 🎉🎉🎉


# Introduction

You need Firebase Authentication for two reasons:

1. Firebase Security Rules rely on Firebase Authentication
2. Firebase Authentication is ridiculously easy to use

## Firebase Auth + Security Rules

Firebase needs a security system. In a traditional database you provide your own security using your API server. Since Firebase **is** the API server, it needs a programmable way to control read and write access to your data.

When users use your client apps to authenticate with Firebase Authentication they receive a [JSON Web Tokens](https://jwt.io/) that will identify them to Firebase's **security rules** system. We'll cover this more later. Just remember that Firebase Authentication will enable your users to interact with the rest of the Firebase platform.

## Ease Of Use

Have you ever implemented your own auth system? Yes? Then you know how challenging it can be. If not... then take my word for it and use an off-the-shelf system. Firebase Authentication provides the following auth methods:

* Email/password
* Phone
* OAuth 2
  * Google
  * Facebook
  * Twitter
  * Github
* Anonymous
* Custom auth

![Imgur](https://i.imgur.com/5K9DW4z.png)

All of these methods use [JSON Web Tokens](https://jwt.io/), also known as JWTs.

> JWT is pronounced the same as the English word "jot".

We'll cover JWTs later. Don't worry. They're simple JSON objects.

### Email/Password

Email/password auth is exactly what it sounds like. You register an email address and a password with Firebase and it keeps track of your user account.

### Phone

Google [acquired Twitter Digits](https://firebase.googleblog.com/2017/01/FabricJoinsGoogle17.html) in early 2017 and rolled their phone authentication into Firebase Authentication. This means that with minimum fuss you can implement a full SMS-based phone auth flow.

This is particularly great for mobile web apps. Phone authentication is the preferred auth method for many users, especially those outside of the United States.

### OAuth 2 (Google, Facebook, Twitter, Github)

OAuth is the fastest auth method, because it relies on accounts that your users already have. Most everyone has either Google, Facebook or Twitter, and developers love Github. OAuth 2 is also the easiest auth flow to implement.

And all of the OAuth providers support [multi-factor authentication](https://en.wikipedia.org/wiki/Multi-factor_authentication), which we should all be using.

## Anonymous Auth

You may want to interact with a client that hasn't authenticated. If you're developing a shopping cart feature for your app, you may want users to add items to their carts before they've created an account. This is where anonymous auth comes in.

Without some sort of authentication, there's no way for your server to know who it's talking to.

Firebase's data is either entirely open or requires authentication. Any private transaction between the Firebase database and a client app requires authentication or it will be public and could be intercepted by a malicious third party.

There are situations in which you might make your data publicly accessible; however, user transactions with your database are unlikely to fit this model... and that's where anonymous auth comes in.

We won't cover anonymous auth any further except to show how dead simple it is to execute:

```javascript
firebase
  .auth()
  .signInAnonymously()
  .then(successHandler, errorHandler);
```

### Custom Tokens

Would you like to use a third-party authentication system with Firebase?

Maybe you'd like to authenticate with your company's existing SAML implementation?

That's not a problem. We won't cover custom tokens here except to say that you can use your private auth servers to mint Firebase auth tokens that you can then send to your client applications.

These tokens will allow your client apps to authenticate with Firebase using whatever JWT you create on your server.

## Link Multiple Auth Providers

Linking multiple auth providers is another topic we won't cover here. Just be aware that you can easily [link multiple auth providers to a single account](https://firebase.google.com/docs/auth/web/account-linking).

For example, your user might register an email/password account and you could prompt her to link her account to Google and Facebook. Your client app can make a couple of easy calls to Firebase Authentication to initiate the linking procedure, and now your user can sign in with Google, Facebook, Twitter or Github.

Alternatively, if your user signed up with an OAuth account, you can prompt her to register a linked email/password combination.


# Walkthrough

## Open the app

Open the app at [fogo.howtofirebase.com](https://fogo.howtofirebase.com/).

## Open DevTools

[Sourcemaps are currently broken in Chrome](https://github.com/webpack/webpack/issues/3165) for Fogo as of July 2018.

This is best done in Firefox.

Right-click `inspect element` and select the `Debugger` tab. Then click `command + P` to search for and open two files:

* `firebase-authentication/src/services/auth.service.js`,
* and `firebase-authentication/src/index.js`.

![firebase-authentication/src/services/auth.service.js](https://goo.gl/KJ2RgR)

![firebase-authentication/src/index.js](https://goo.gl/KJ2RgR)

## Video

{% embed url="<https://youtu.be/m1rlD-8z3Yo>" %}


# Challenge

## Firebase Authentication

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [challenge-authentication](https://github.com/how-to-firebase/firelist-react/tree/challenge-authentication).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout challenge-authentication
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`

## Start the app

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then run `yarn start` or `npm run start` to spin up the development server.

```
yarn
yarn start
```

## Complete the challenge

Search the codebase for `Challenge Authentication` to find all of the challenges.

We'll be editing `./src/components/authenticate.js` and `./src/index.js`.

Read the comments and complete the steps in those two files.


# Notes

## Firebase Authentication

See the [Firebase Authentication docs for web](https://firebase.google.com/docs/auth/web/manage-users).

### onAuthStateChanged

```javascript
firebase.auth().onAuthStateChanged(currentUser => {
  if (currentUser) {
    // User is signed in.
  } else {
    // No user is signed in.
  }
});
```

### Register Email/Password

```javascript
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // ...
});
```

### Sign In Email/Password

```javascript
firebase.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // ...
});
```

### Create Provider

Google

```javascript
var provider = new firebase.auth.GoogleAuthProvider();
```

Facebook

```javascript
var provider = new firebase.auth.FacebookAuthProvider();
```

Twitter

```javascript
var provider = new firebase.auth.TwitterAuthProvider();
```

GitHub

```javascript
var provider = new firebase.auth.GithubAuthProvider();
```

### OAuth sign in with a provider

Popup

```javascript
firebase.auth().signInWithPopup(provider);
```

Redirect

```javascript
firebase.auth().signInWithRedirect(provider);
```

### Phone Auth

First attach a recaptcha using an element ID...

```javascript
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
  'size': 'invisible',
  'callback': function(response) {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});
```

... then capture a phone number from user input and send the sms...

```javascript
var phoneNumber = getPhoneNumberFromUserInput();
var appVerifier = window.recaptchaVerifier;
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
  .then(function (confirmationResult) {
    // SMS sent. Prompt user to type the code from the message, then sign the
    // user in with confirmationResult.confirm(code).
    window.confirmationResult = confirmationResult;
  }).catch(function (error) {
    // Error; SMS not sent
    // ...
  });
```

...and finally authenticate with the code from user input.

```javascript
var code = getCodeFromUserInput();
confirmationResult.confirm(code).then(function (result) {
  // User signed in successfully.
  var user = result.user;
  // ...
}).catch(function (error) {
  // User couldn't sign in (bad verification code?)
  // ...
});
```


# Introduction

Cloud Firestore is the successor to both Cloud Datastore and the original Firebase Realtime Database.

Cloud Datastore already powers massive web applications, and the Realtime Database proved how amazing direct browser-to-database interactions can be. Cloud Firestore combines the scalability and reliability of Cloud Datastore with the awesome client libraries of the Realtime Database to create a massively scalable, easily-to-develop cloud database platform.

## Document Collection

Cloud Firestore is a document/collection database like MongoDB or CouchDB. Document/collection means that you create collections and nest documents within those collections. A nested document may itself have a sub-collection of documents.

Unlike a relational database, there are no explicit connections between documents besides this parent/child relationship. If you'd like one document to reference another document, you'll need to save the target document's ID to a field in the referencing document.

Imagine a data structure like this:

* Users
  * userId
  * email
* Transactions
  * userId
  * amount

In this case we have a collection of Users and each user document has a `userId` and an `email`. Likewise, we have a collection of Transactions where each transaction has a `userId` and an `amount`.

The `userId` fields can be used to connect records in the two collections, but it's an informal connection defined by your application logic, not by the database itself.

In a relational database you would be able to join Users to Transactions based on the shared userId key. In a document/collection DB that is not possible. Instead, you'd run two queries, one on each collection, and then join the records manually.

## Why not just a relational database?

Relational databases and document/collection databases have quite a bit of overlap. Frankly, for most use cases, either type of database will be just fine... given that you structure your data correctly :)

The advantages of document/collection databases like Firestore is that it is easier to scale, because each document can live on its own. Your database doesn't have to lock down a bunch of relationships to replicate data... it just copies documents across servers. We recognize that Google Spanner has achieved similar performance for relational data, so this isn't a pure win for the document/collection model; however, document/collection does make possible client libraries like the Firestore SDK.

See, relational databases require schemas. Relational database require table updates. Document/collection databases can be flexible, meaning that you can save whatever data you like in a document. The database just does not care about document structure.

So I could make a collection of Foods, and each Food could have entirely different attributes... and the database—like the honey badger—don't care.

* Foods
  * Spaghetti
    * noodleType
    * sauceFlavor
  * Sandwich
    * breadType
    * hasPickles

## What is a client library?

Traditional application architecture includes a layer of servers that talk to your database. So your client—in this case, a browser—will make an HTTP call to your server, which will perform security checks, query the database, and return results to the client.

Serverless application architecture has the browser communicating directly with the database. The database runs all of its own security checks and serves up data as requested.

The Firestore SDK is a client library that enables your browser to talk directly to the Cloud Firestore database. Firestore handles all of its own security, and the Firestore SDK can read and write whatever your browser asks it to.

This is huge, because you don't have to write any server code. All of that time crafting REST APIs or implementing GraphQL is unnecessary!

## Limitations of Cloud Firestore

Cloud Firestore is not great for highly relational data.

[Relational databases](https://cloud.google.com/sql/) are optimized for relational data. For example, if you're building an inventory system and your inventory numbers are constantly changing, and each inventory item can belong to a number categories, and you need to be able to quickly run reports on different stock levels for different category groups... you might want to stick to a relational database.

Cloud Firestore can be sub-optimal for graph data.

Graph databases such as [JanusGraph](http://janusgraph.org/) are optimized for graph data. For instance, if you're building a social app and you find yourself trying to query how many of a user's friends live in nearby cities, but you also need to query how many of those friends' friends also live nearby... you may want to stick to a graph database.

Both of these use cases--relational and graph data--**can** be modeled in Cloud Firestore; however, the queries may run more slowly and cost more than if you used a more dedicated database.

## Hybrid solutions

Just because you need a relational database for parts of your data does not mean that you must abandon Firestore!

Most large web applications use a variety of datastores. For instance, a website may use Redis for caching, GraphQL + JanusGraph + Cassandra for social data, SQL for financial data, and Firestore for everything else. Yes, this sort of model adds complexity, and we don't recommend introducing any more complexity to your app than is necessary; however, sometimes complex problems require complex solutions.

Yes, you can drive a large truck to work every day. No, that truck will not be as efficient as driving a small sedan. And yes, a small sedan can transport two cubic yards of sand if you take five trips, but nobody does that. If you need that much sand, you'll rent a truck.

We recommend starting with Firestore until you discover that it won't meet your needs. You may be surprised at how flexible and powerful Firestore can be, and you may never find the need for another database :)

## But how much does it cost???

Cloud Firestore is built for large quantities of data. It's no BigQuery... but it's a great place to store your live application data. You'll get billed based on how many reads, writes and deletes you complete, as well as a very economical storage fee based on the gigabytes of data in the database.

Just note that if you write a query that returns 100 results, you've just logged 100 reads. As of this writing, 100k reads costs $0.06... so most apps should be fine. Just be take care not to build too "chatty" of an application. If it gets a lot of users, you may see your bills jump.

The Realtime Database is a much better home for high read/write data. Use Cloud Firestore for everything else.


# Walkthrough

## Open the app

Open the app at [fogo.howtofirebase.com](https://fogo.howtofirebase.com/).

## Open DevTools

[Sourcemaps are currently broken in Chrome](https://github.com/webpack/webpack/issues/3165) for Fogo as of July 2018.

This is best done in Firefox.

Right-click `inspect element` and select the `Debugger` tab. Then click `command + P` to search for and open two files:

* `observers/images.observer.js`,
* and `queries/images.query.js`.

![observers/images.observer.js](https://goo.gl/DeEsZT)

![queries/images.query.js](https://goo.gl/pMa7ad)

## Video

{% embed url="<https://youtu.be/eOXlUYruPjw>" %}


# Security Rules

A public-facing database wouldn't be complete without a security system.

Firestore and Firebase Storage both use Firebase's new security rules syntax, while the original Firebase Realtime Database uses the original JSON security rules syntax. Both systems are easy enough to work with.

The gist of security rules is that you'll be granting read and/or write access to individual nodes of your database.

## Basic Rules

Our Firestore security rules for Fogo, our image-sharing app, are as follows:

```
service cloud.firestore {
  match /databases/{database}/documents {
    match /uploads/{document=**} {
      allow write: if request.auth.token.admin == true ;
      allow read;
    }

    match /users/{document=**} {
      allow read, write: if request.auth.token.admin == true ;
    }
  }
}
```

Let's break these rules down line-by-line.

**service cloud.firestore** — defines the service, in this case it's `cloud.firestore`

**match /databases/{database}/documents** — defines the database; the `{database}` clause indicates that these rules apply to all Firestore databases on the project

**match /uploads/{document=\*\*}** — creates a new rules block to apply to the *uploads* collection and all documents contained therein

**allow write: if requests.auth.token.admin == true ;** — allows write access for authenticated sessions with an `admin` attribute equal to `true` on the auth token, which is also known as the user's JWT

**allow read;** — allows public read access

**match /users/{document=\*\*}** - creates a new rules block for the *users* collection and all documents contained therein

**allow read, write: if request.auth.token.admin == true ;** - allows both read and write access for authenticated sessions with an `admin` attribute equal to `true` on the auth token, which is also known as the user's JWT

## Match blocks

Here's the pattern for match blocks 👇

```
match /my-collection/my-document {

}
```

The document-name fields can be set to wildcard values as well, which looks like this 👇

```
match /my-collection/{allDocuments} {

}
```

If you want a rule to apply to all documents *AND* all sub-collection documents, you need a slightly different syntax:

```
match /my-collection/{allDocuments=**} {

}
```

Notice the `=**` at the end of the wildcard? That's the "recursive wildcard" syntax. If you don't tell your wildcard to be recursive, your match block will not apply to sub-collection documents. Imagine a data structure like `/users/{userDocs}/preferences/{preferenceDocs}`, where you have a collection of Users, and each user has a collection of Preferences. We can think of three types of match blocks for this data structure:

```
match /users/{user} {
  // applies to the user docs, NOT the nested preference docs
}

match /users/{user=**} {
  // applies to just the user docs AND the nested preference docs
}

match /users/{user}/preferences/{preference} {
  // applies ONLY to the preference docs
}
```

You could write the same rules like this:

```
match /users/{user} {
  // applies to the user docs, NOT the nested preference docs

  match /preferences/{preference} {
    // applies only to the preference docs
  }
}
```

See what we did there with the nested rule blocks? Yeah, you can nest match blocks. It's purely optional, but it might be easier to read in some cases.

## Rule types

The basic rule types are `read` and `write`. But each of these rule types can be broken down into sub-types.

* read
  * get
  * list
* write
  * create
  * update
  * delete

### Read rules

The basic `allow read` rule grants both `get` and `list` access to the documents in a collection.

The `allow get` rule allows a user to read a single document, but not list all documents.

And `allow list` allows a user to read an entire collection or query the collection.

There's an funny bit of detail here. Imagine allowing a user to `list` a collection but not `get` a document within it. This would be a strange rule, because that user would still be able to read each individual document... but he or she would have to pull the entire collection rather than one specific document. There might be a use case for this pattern, but we can't think of one.

The `get` and `list` rules would be useful if, for example, you want an admin user to both get and list all of the documents in a collection, but you want regular users to only get certain documents that are specific to them. In this model, you'd have to provide the ID of the document to the user.

Here's how those queries could look:

```javascript
firebase
  .firestore()
  .collection('users')
  .get()
  .then(snapshot => {
    // allowed for an admin user
  })
  .catch(error => {
    // a non-admin user is denied list permission
  });

firebase
  .firestore()
  .collection('users')
  .doc('my-user-id')
  .get()
  .then(snapshot => {
    // a non-admin user can get just one doc
  });
```

### Write rules

The `allow write` rule grants `create`, `update` and `delete` privileges.

The `allow create`, `allow update` and `allow delete` rules are self-explanatory 😊

Imagine a project-management app with three levels of user-security: admins, managers and employees. In this case, you may want to allow employees to update existing projects, allow managers to create and update projects and allow the admins full create, update and delete permissions.

## Conditions

Perhaps the trickiest part of security rules is writing the individual rule conditions.

To start off, you can always declare a rule without conditions:

```
match /dropboxCollection {
  read false;
  write true;
}

match /mailboxCollection {
  read true;
  write false;
}
```

> ***Note:*** Your Cloud Functions and authorized Node.js servers have full read/write access to the entire database, regardless of security rules.

But most apps need write conditions, so security rules have a similar-to-JavaScript DSL (domain-specific language) for defining those conditions.

## Wildcard variables

First off, any wildcards that you declared in your match rules are available as variables.

The following example shows how you could enable users to read their own user documents and write only one preference document: `receiveMarketingEmail`;

```
match /users/{userId} {

  allow read: if request.auth.uid == userId;

  match /preferences/{preference} {  
    allow read: if request.auth.uid == userId;
    allow write: if request.auth.uid == userId && preference == 'receiveMarketingEmail';
  }
}
```

## Request variables

Rule conditions have access to a `request` object that represents that incoming request. You'll be using the `request` object for most rule conditions. Here's a sketch of what that object looks like as JSON:

```javascript
{
  "auth": {
    "uid": "my-unique-user-id-aka-uid",
    "token": {
      "some-custom-claim": true,
      "email": "user@email.com",
      "email_verified": false,
      "phone_number": null,
      "name": "My displayName",
      "sub": "my-unique-user-id-or-uid",
      "firebase": {
        "identities": {
          "google.com": ["first-google-uid", "second-google-uid"],
          "facebook.com": ["first-facebook-uid", "second-facebook-uid"]
        },
        "sign_in_provider": "facebook.com"
      }
    }
  },
  "path": Path,
  "query": {
    "limit": 10,
    "offset": "some-cursor-value",
    "groupBy": {
      "widgetType": true,
      "widgetName": false
    },
    "orderBy": {
      "widgetCreated": "ASC",
      "widgetName": "DESC"
    },
    "resource": Resource
  },
  "time": Timestamp,
  "writeFields": List
}
```

The `request.auth` attribute is mostly primitive values, i.e., strings, numbers, nulls and booleans. Note that you can access custom claims directly off of `request.auth.token`.

Now we get into some custom objects. You'll want to follow links and read the docs.

`request.path` is a [Path object](https://firebase.google.com/docs/firestore/reference/security/#path_2).

`request.query.resource` is a [Resource objects](https://firebase.google.com/docs/firestore/reference/security/#firestore-resource) representing the changes being made by a write.

`request.time` is a [Timestamp object](https://firebase.google.com/docs/firestore/reference/security/#timestamp).

And finally, `request.writeFields` contains a [List object](https://firebase.google.com/docs/firestore/reference/security/#list) of fields being written.

## Resource object

In addition to the `request` object, there's also a `resource` object available. That means that `resource.data` can be compared to `request.resource.data` to identify requested changes.

## Stick with request.auth

The `request.auth` object is by far the most commonly used part of the `request` object, especially when [custom claims](https://firebase.google.com/docs/auth/admin/custom-claims) have been set on `request.auth.token`.

A common custom claim pattern is to set an `admin` flag which can be used like so:

```
match /superSecretAdminStuff/{docs=**} {
  allow read, write: request.auth.token.admin == true;
}
```

## Read the docs

Firebase has excellent docs, and we don't want to compete with such great writing.

The highlights:

* [the Resource object](https://firebase.google.com/docs/firestore/reference/security/#firestore-resource)
* [Functions: exists() and get()](https://firebase.google.com/docs/firestore/reference/security/#functions)
* [Developer-defined Functions](https://firebase.google.com/docs/firestore/reference/security/#developer_defined)
* [Data types](https://firebase.google.com/docs/firestore/reference/security/#data_types)

## Firebase Storage security rules are nearly identical

Firestore's security system is shared by [Firebase Storage](https://firebase.google.com/docs/reference/security/storage/). The objects and data types are identical, but Storage is dealing with binary objects, so it's Resource properties are bit different.


# Indexes

Indexes are required in Cloud Firestore whenever you want to use two where-clauses in a single query.

For example, the following query filters a collection called `uploads` by a `userId` and also orders by a `created` field:

```javascript
async function getUploadsByUser(userId) {
  const query = window.firebase
    .firestore()
    .collection('uploads')
    .where('userID', '==', userId)
    .orderBy('created', 'desc');
  const snapshot = await query.get();
  return snapshot.docs.map(doc => doc.data());
}
```

This query would require indexes on `userId` and `created`.

## Console index management

Queries are super easy to handle via the Console.

The easiest way to do it is to write whatever queries you like and watch your DevTools console for error messages.

You'll see an error message in DevTools whenever you attempt to run a query that requires an index. That error message will come with a link that you can click on to take you to your Console.

Once on the Console, you'll get a prompt to create the required index. Simply confirm and wait for the index to build.

## Firebase Tools index management

The other way to manage indexes is via Firebase Tools.

When you run `firebase init` in your project folder and opt-in to using Firestore, you'll get a file in your project folder named `firestore.indexes.json`.

You'll spec out your indexes in `firestore.indexes.json` and run `firebase deploy` or `firebase deploy --only firestore` to deploy your indexes.

Here's a sample of how your indexes file could look:

```javascript
{
  "indexes": [
    {
      "collectionId": "uploads",
      "fields": [
        { "fieldPath": "userId", "mode": "ASCENDING" },
        { "fieldPath": "created", "mode": "DESCENDING" }
      ]
    }
  ]
}
```

## And that's it!

There's nothing more to Firestore indexes. You just need to be aware of them and adjust them as necessary.


# Challenge

## Cloud Firestore

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [challenge-firestore](https://github.com/how-to-firebase/firelist-react/tree/challenge-firestore).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout challenge-firestore
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`

## Start the app

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then run `yarn start` or `npm run start` to spin up the development server.

```
yarn
yarn start
```

## Complete the challenge

Search the codebase for `Challenge Firestore` to find all of the challenges.

Read the comments and complete the steps in those files.


# Notes

## Cloud Firestore

See the [Cloud Firestore docs for web](https://firebase.google.com/docs/firestore/quickstart).

### Set a document

```javascript
var data = {
  name: 'Los Angeles',
  state: 'CA',
  country: 'USA',
};

// Add a new document in collection "cities" with ID 'LA'
var setDoc = db
  .collection('cities')
  .doc('LA')
  .set(data);
```

### Data types

```javascript
var data = {
  stringExample: 'Hello, World!',
  booleanExample: true,
  numberExample: 3.14159265,
  dateExample: new Date('December 10, 1815'),
  arrayExample: [5, true, 'hello'],
  nullExample: null,
  objectExample: {
    a: 5,
    b: true,
  },
};

var setDoc = db
  .collection('data')
  .doc('one')
  .set(data);
```

### Add document with auto-generated ID

> In a single-step with **asynchronous** access to the new ref

```javascript
// Add a new document with a generated id.
var addDoc = db
  .collection('cities')
  .add({
    name: 'Tokyo',
    country: 'Japan',
  })
  .then(ref => {
    console.log('Added document with ID: ', ref.id);
  });
```

> In two steps with **synchronous** access to the new ref

```javascript
// Add a new document with a generated id.
var newCityRef = db.collection('cities').doc();

console.log('newCityRef id:', newCityRef.id);

var setDoc = newCityRef
  .set({
    name: 'Tokyo',
    country: 'Japan',
  })
  .then(ref => {
    //...
  });
```

### Update document

Note the optional `merge: true` option

```javascript
var cityRef = db.collection('cities').doc('DC');

// Set the 'capital' field of the city
var updateSingle = cityRef.update({ capital: true }, { merge: true });
```

### Transactions

```javascript
// Initialize document
var cityRef = db.collection('cities').doc('SF');
var setCity = cityRef.set({
  name: 'San Francisco',
  state: 'CA',
  country: 'USA',
  capital: false,
  population: 860000,
});

var transaction = db
  .runTransaction(t => {
    return t.get(cityRef).then(doc => {
      // Add one person to the city population
      var newPopulation = doc.data().population + 1;
      t.update(cityRef, { population: newPopulation });
    });
  })
  .then(result => {
    console.log('Transaction success!');
  })
  .catch(err => {
    console.log('Transaction failure:', err);
  });
```

### Batched writes

```javascript
// Get a new write batch
var batch = db.batch();

// Set the value of 'NYC'
var nycRef = db.collection('cities').doc('NYC');
batch.set(nycRef, { name: 'New York City' });

// Update the population of 'SF'
var sfRef = db.collection('cities').doc('SF');
batch.update(sfRef, { population: 1000000 });

// Delete the city 'LA'
var laRef = db.collection('cities').doc('LA');
batch.delete(laRef);

// Commit the batch
return batch.commit().then(function() {
  // ...
});
```

### Bulk delete

> Max batch size is 500 records

```javascript
function deleteCollection(db, collectionPath, batchSize) {
  var collectionRef = db.collection(collectionPath);
  var query = collectionRef.orderBy('__name__').limit(batchSize);

  return new Promise((resolve, reject) => {
    deleteQueryBatch(db, query, batchSize, resolve, reject);
  });
}

function deleteQueryBatch(db, query, batchSize, resolve, reject) {
  query
    .get()
    .then(snapshot => {
      // When there are no documents left, we are done
      if (snapshot.size == 0) {
        return 0;
      }

      // Delete documents in a batch
      var batch = db.batch();
      snapshot.docs.forEach(doc => {
        batch.delete(doc.ref);
      });

      return batch.commit().then(() => {
        return snapshot.size;
      });
    })
    .then(numDeleted => {
      if (numDeleted === 0) {
        resolve();
        return;
      }

      // Recurse on the next process tick, to avoid
      // exploding the stack.
      process.nextTick(() => {
        deleteQueryBatch(db, query, batchSize, resolve, reject);
      });
    })
    .catch(reject);
}
```

### Get a document

```javascript
var cityRef = db.collection('cities').doc('SF');
var getDoc = cityRef
  .get()
  .then(doc => {
    if (!doc.exists) {
      console.log('No such document!');
    } else {
      console.log('Document data:', doc.data());
    }
  })
  .catch(err => {
    console.log('Error getting document', err);
  });
```

### Get an entire collection

```javascript
var citiesRef = db.collection('cities');
var allCities = citiesRef
  .get()
  .then(snapshot => {
    snapshot.forEach(doc => {
      console.log(doc.id, '=>', doc.data());
    });
  })
  .catch(err => {
    console.log('Error getting documents', err);
  });
```

### Get with a where clause

```javascript
var citiesRef = db.collection('cities');
var query = citiesRef
  .where('capital', '==', true)
  .get()
  .then(snapshot => {
    snapshot.forEach(doc => {
      console.log(doc.id, '=>', doc.data());
    });
  })
  .catch(err => {
    console.log('Error getting documents', err);
  });
```

### List subcollections

```javascript
var sfRef = db.collection('cities').doc('SF');
sfRef.getCollections().then(collections => {
  collections.forEach(collection => {
    console.log('Found subcollection with id:', collection.id);
  });
});
```

### Listen for document changes

```javascript
var doc = db.collection('cities').doc('SF');

var observer = doc.onSnapshot(
  docSnapshot => {
    console.log(`Received doc snapshot: ${docSnapshot}`);
    // ...
  },
  err => {
    console.log(`Encountered error: ${err}`);
  }
);
```

### Listen for collection changes

```javascript
var query = db.collection('cities').where('state', '==', 'CA');

var observer = query.onSnapshot(
  querySnapshot => {
    console.log(`Received query snapshot of size ${querySnapshot.size}`);
    // ...
  },
  err => {
    console.log(`Encountered error: ${err}`);
  }
);
```

### Stop listening

```javascript
var unsub = db.collection('cities').onSnapshot(() => {});

// ...

// Stop listening for changes
unsub();
```

### Compound queries

> Valid queries

```javascript
citiesRef.where('state', '==', 'CO').where('name', '==', 'Denver');

citiesRef.where('state', '==', 'CA').where('population', '<', 1000000);

citiesRef.where('state', '>=', 'CA').where('state', '<=', 'IN');

citiesRef.where('state', '==', 'CA').where('population', '>', 1000000);
```

> !!! INVALID QUERY AHEAD !!!

```javascript
// Invalid query. Will throw an error.
citiesRef.where('state', '>=', 'CA').where('population', '>', 1000000);
```

### Order and limit

> Valid order/limit combinations

```javascript
var firstThree = citiesRef.orderBy('name').limit(3);

var lastThree = citiesRef.orderBy('name', 'desc').limit(3);

var byStateByPop = citiesRef.orderBy('state').orderBy('population', 'desc');

var biggest = citiesRef
  .where('population', '>', 2500000)
  .orderBy('population')
  .limit(2);

var allBigCities = citiesRef.where('population', '>', 2500000).orderBy('population');
```

> !!! INVALID QUERY AHEAD !!!

```javascript
// Invalid query. Will throw an error.
citiesRef.where('population', '>', 2500000).orderBy('country');
```

### Pagination: single-cursor

> Valid pagination

```javascript
var startAt = db
  .collection('cities')
  .orderBy('population')
  .startAt(1000000);

var startAfter = db
  .collection('cities')
  .orderBy('population')
  .startAfter(1000000);

var endAt = db
  .collection('cities')
  .orderBy('population')
  .endAt(1000000);

var endBefore = db
  .collection('cities')
  .orderBy('population')
  .endBefore(1000000);
```

### Pagination: multiple-cursors

```javascript
// Will return all Springfields
var startAtName = db
  .collection('cities')
  .orderBy('name')
  .orderBy('state')
  .startAt('Springfield');

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
var startAtNameAndState = db
  .collection('cities')
  .orderBy('name')
  .orderBy('state')
  .startAt('Springfield', 'Missouri');
```


# Introduction

The Firebase Realtime Database (aka "the RTDB") is the original database that shot Firebase onto the scene in 2012.

The RTDB was one of the first "serverless" datastores on the market, competing directly with Parse and eventually with RethinkDB's Horizon project.

The original innovation was to enable web browsers to connect directly to the database with a realtime web socket connection. Browsers have made HTTP calls out to API services for years, but now, instead of making manual API calls, your browser could subscribe to the database directly and receive live updates.

## JSON Datastore

The RTDB is neither a relational database nor a document/collection datastore like Cloud Firestore.

The RTDB is a single JSON object with up to 32 levels of depth, and you can subscribe to any node of that JSON object, whether it already exists or has yet to be made.

This sort of JSON datastore makes the RTDB entirely unstructured. You can define whatever data structures you prefer in your browser and then save those data structures directly to the RTDB without modification and without telling the RTDB what the data will look like.

Unstructured data can make development lightning fast... if you know what you're doing.

If you're naive in how you structure your data, then you're in for very unpleasant surprises.

## Strengths

The Realtime Database is ideal for realtime interactions. You could write your own web sockets server, but why? Web sockets are wicked hard to get working smoothly, and Firebase has already done the work for you.

The RTDB is great for small, fast transactions. Think of the ideal RTDB use case as streaming data. You stream data to the RTDB and it streams that data out to all connected clients in a single, realtime stream.

In fact, the entire Realtime Database SDK is evented. It treats data as event streams, not as fixed queries.

## Weaknesses

RTDB devotees such as ourselves spent the years of 2012 through 2017 learning to model all sorts of creative data structures in pure JSON. It hasn't always been easy.

We learned to host entire production applications on the RTDB long before Firestore came along to give us a more structured data model. We can model almost anything in JSON...but we don't have to do that anymore, so we'll completely avoid the RTDB's weaknesses by using Firestore for all of those use cases.

## Why is the Realtime Database so limited?

The RTDB was designed for lightning fast updates. That primary design constraint prevented the Firebase team from developing sophisticated query capabilities.

You can execute a single order-by filter and a single limit filter on each RTDB data stream. So you can order a list of movies by release date and request a live stream of the 10 most recent movies, but you can't also filter that stream to include only action movies. You already used your one order-by filter on the release date. Tough luck!

Of course, you **can** create a new collection of movieTypes, each of which has movies ordered by release date... so your movieTypes could include action, drama and comedy, in which case you could then query the action movies list and order by release date.

That achieves the same goal, but it adds a bunch of complexity to how you write your data, because now each movie has to be duplicated from the primary movies list to the appropriate movieTypes list. And what if you also want to filter by MPAA ratings? Get prepared to duplicate your data once again.

## How do we use the Realtime Database?

We use the RTDB for streaming short-lived, constantly-changing data.

We use it for tracking logged-in users.

We use it for live progress notifications for long-running Cloud Functions.

We use it for Cloud Functions job queues.

And that's about it.

There are plenty of other applications where the RTDB still outperforms Cloud Firestore, and we still have plenty of apps in production that use the RTDB exclusively; however, our new projects store >90% of their data in Cloud Firestore.

## Realtime Database Cost

The RTDB is billed based on the gigabytes of data stored and the gigabytes transferred. This makes it ideal for smaller datasets with massive transaction counts. The RTDB doesn't care how many reads and writes you make. It cares about the volume of data travelling over the network.

Don't store large volumes of data in the RTDB. That's not what it's built for, and you'll pay $5/GB every month. That's nothing if you're streaming bytes of short-lived data. Use Cloud Firestore for everything else.


# Walkthrough

## Open the app

Open the app at <https://glitch.com/~truth-spleen>.

## Open DevTools

Right-click `inspect element` and select the `Debugger` tab. Then click `command + P` to search for and open two files:

* `src/components/chat-form.js`,
* and `src/components/chat-wrapper.js`.

![src/components/chat-form.js](https://goo.gl/pRKRpS)

![src/components/chat-wrapper.js](https://goo.gl/u7vFtm)

## Video

{% embed url="<https://youtu.be/OZsAHXAbPP8>" %}


# Security Rules

The Realtime Database is public facing, so it needs a robust security system.

Firestore and Firebase Storage both use Firebase's new security rules syntax, which we've covered elsewhere. Those learnings won't transfer to the RTDB, because the RTDB's security rules were designed back in 2011 and are specific to its JSON data model.

## Follow the JSON

The RTDB stores data in JSON. It's security rules are also written in JSON and follow the pattern of the data that you plan on storing.

The starting JSON rules object is pretty simple:

```javascript
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}
```

Notice that there's a root attribute named `rules` and there are two kinds of permissions, `.read` and `.write`. There's also an `auth` object available in the rule conditions which we can test to make sure that it's not null. If it's not null, then the request must be coming from an authenticated user!

Now let's secure the `users` node on our imaginary JSON tree.

**Data**

```javascript
{
  "users": {
    "userOne": {...},
    "userTwo": {...}
  }
}
```

**Rules**

```javascript
{
  "rules": {
    "users": {
      ".read": "auth != null",
      ".write": false
    }
  }
}
```

Notice how we created a new node under `rules` and we called that node `users`? Yeah, we're nesting our rules to match our data. So `rules.users` matches our `users` node in the JSON.

## Rule types

The RTDB has only three rule types:

* `.read`
* `.write`
* `.validate`

`.read`, `.write` and `.validate` must all be set to `true`, `false` or a string "condition" that the security rules engine will need to evaluate.

`.read` and `.write` are simple enough. They grant read or write access.

`.validate` will prevent a write if it's condition statement evaluates to false.

The RTDB has one more type, although we wouldn't call it a rule so much as a directive:

* `.indexOn`

The `.indexOn` field is necessary to speed up RTDB queries, and it's either the string `".value"` or an array of strings representing the attributes to index.

`.indexOn` could be used like this:

```javascript
{
  "rules": {
    "scores": {
      ".indexOn": ".value"
    },
    "teams": {
      ".indexOn": ["ranking", "dateCreated", "mascotColor"]
    }
  }
}
```

## Wildcards

Wildcards are an important concept in RTDB security rules. Here's an example of a `$userId` wildcard:

```javascript
{
  "rules": {
    "users": {
      "$userId": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$userId === auth.uid"
      }
    }
  }
}
```

The dollar sign in `$userId` indicates that it's a wildcard. You an name wildcards whatever you like. You could have named it `$broccoli`... but then you'd have to refer to the wildcard as `broccoli` in your condition statements :)

Wildcards apply to all otherwise-unspecified attributes. So assume the following data:

```javascript
{
  "users": {
    "userOne": {...},
    "userTwo": {...},
    "userThree": {...},
  }
}
```

We have three users. Now let's imagine all user data is public except that of our admin, `userThree`. Also let's assume that we want `userThree` to be able to write to everyone's records as well as read and write to her own.

```javascript
{
  "rules": {
    "users": {
      "$userId": {
        ".read": true,
        ".write": "auth.uid === 'userThree'"
      },
      "userThree": {
        ".read": "auth.uid === 'userThree'",
        ".write": "auth.uid === 'userThree'"
      }
    }
  }
}
```

We used the `$userId` wildcard to set rules for all users. Then, by specifying `rules.users.userThree`, we overrode the wildcard for `userThree`.

## Cascading rules

Be very aware when designing your data models that RTDB security rules cascade.

Let's modify the earlier example a bit and try to block all users from reading `users.$userId.superSecretUserAttributes`.

```javascript
{
  "rules": {
    "users": {
      "$userId": {
        ".read": true,
        ".write": "auth.uid === 'userThree'",
        "superSecretUserAttributes": {
          ".read": "auth.uid === 'userThree'"
        }
      }
    }
  }
}
```

Remember how rules cascade? Well, our attempt at securing `superSecretUserAttributes` just failed, because we granted `.read` access higher up the chain. In this example, all users can still read `superSecretUserAttributes` and the nested `.read` rule gets ignored.

## Design your data with cascading rules in mind

Cascading security rules mean that you should design your data structure such that you never need to nest security rules. Here's our favorite way to write rules:

```javascript
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid === $uid || auth.token.admin === true",
        ".write": "auth.uid === $uid || auth.token.admin === true"
      }
    },
    "userOwned": {
      "$objectType": {
        "$uid": {
          ".read": "auth.uid === $uid || auth.token.admin === true",
          ".write": "auth.uid === $uid || auth.token.admin === true"
        }
      }
    },
    "userReadable": {
      "$objectType": {
        "$uid": {
          ".read": "auth.uid === $uid || auth.token.admin === true",
          ".write": "auth.token.admin === true"
        }
      }
    },
    "userWriteable": {
      "$objectType": {
        "$uid": {
          ".read": "auth.token.admin === true",
          ".write": "auth.uid === $uid || auth.token.admin === true"
        }
      }
    },
    "adminOwned": {
      "$objectType": {
        "$uid": {
          ".read": "auth.token.admin === true",
          ".write": "auth.token.admin === true"
        }
      }
    },
    "public": {
      "$objectType": {
        "$uid": {
          ".read": true,
          ".write": "auth.token.admin === true"
        }
      }
    }
  }
}
```

And that's it! We have six base nodes in our data structure:

```javascript
{
  "users": {...},
  "userOwned": {...},
  "userReadable": {...},
  "userWriteable": {...},
  "adminOwned": {...},
  "public": {...}
}
```

And notice how each base node has a wildcard `$objectType` nested directly underneath it? That's so we can save lots of different types of objects, all of which will inherit their rules from their parent nodes.

Instead of fighting the cascading rules, we're using them to our advantage and dramatically reducing the number of rules that we write.

We're leveraging custom claims to permit users with the `admin` claim to read and write everything using `auth.token.admin === true`. [Custom claims](https://firebase.google.com/docs/auth/admin/custom-claims) are awesome, because they're available across all three types of security rules—Firestore, RTDB and Storage—as well as your browser's `currentUser` JWT.

## Validation

Frankly, we do most of our validation in our client-side applications; however, the RTDB's `.validate` security rule will let you do validation right at your data layer.

If you find yourself in need of validation, we recommend reading the [reference docs](https://firebase.google.com/docs/reference/security/database/#validate) carefully. We also recommend not getting too carried away with validation in the security rules. These rules are better seen as a flexible way to add security to your app. If you have some sensitive data or operations and you need more assurances than the `.read` and `.write` rules can give you, it's time for some `.validate` rules.

We're sure that we *could* write a sophisticated validation layer using the security rules, but we don't. We use client-side validation >99% of the time, because the vast majority of our writes aren't likely to be abused by an attacker. Any competent hacker will connect directly to your database and start testing your endpoints, so client-side validation won't stop hacking; however, it's easy enough to hide anything worth hacking deep within a cloud function or an admin-only data node, so try that first before relying on validation rules.


# Challenge

## Realtime Database

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [challenge-rtdb](https://github.com/how-to-firebase/firelist-react/tree/challenge-rtdb).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout challenge-rtdb
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`

## Start the app

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then run `yarn start` or `npm run start` to spin up the development server.

```
yarn
yarn start
```

## Complete the challenge

Search the codebase for `Challenge Realtime DB` to find all of the challenges.

Read the comments and complete the steps in those files.

* `database.rules.json`
* `src/database/set-user-tokens.js`


# Notes

## Realtime Database

See the [Realtime Database docs for web](https://firebase.google.com/docs/database/).

### Set a ref

```javascript
function writeUserData(userId, name, email, imageUrl) {
  firebase
    .database()
    .ref('users/' + userId)
    .set({
      username: name,
      email: email,
      profile_picture: imageUrl,
    });
}
```

### Value events

Value events fire with the entire data payload for any and all changes

> Listen to ongoing events

```javascript
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', function(snapshot) {
  updateStarCount(postElement, snapshot.val());
});
```

> Listen to a single event and stop listening

```javascript
var userId = firebase.auth().currentUser.uid;
return firebase
  .database()
  .ref('/users/' + userId)
  .once('value')
  .then(function(snapshot) {
    var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
    // ...
  });
```

### Multi-path updates

```javascript
function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture,
  };

  // Get a key for a new Post.
  var newPostKey = firebase
    .database()
    .ref()
    .child('posts')
    .push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase
    .database()
    .ref()
    .update(updates);
}
```

### Delete data

```javascript
function deleteUser(userId) {
  return firebase
    .database()
    .ref('/users/' + userId)
    .remove();
}
```

### Detach listener

```javascript
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
var listener = starCountRef.on('value', function(snapshot) {
  updateStarCount(postElement, snapshot.val());
});

function detachListener() {
  starCountRef.off('value', listener);
}
```

### Transactions

```javascript
function toggleStar(postRef, uid) {
  postRef.transaction(function(post) {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}
```

### Child events

* **child\_added**: fires once for every existing result and then again for every new result; does not fire for changes or removals, only new records
* **child\_changed**: fires when the underlying object or value is changed in any way
* **child\_removed**: fires when the entire record is removed

```javascript
var commentsRef = firebase.database().ref('post-comments/' + postId);
commentsRef.on('child_added', function(data) {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_changed', function(data) {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_removed', function(data) {
  deleteComment(postElement, data.key);
});
```

### Sort data

* **orderByChild('childName')**: Orders by a child attribute
* **orderByKey()**: Orders by record keys
* **orderByValue()**: Orders by record values; only relevant when values are strings or numbers and not nested objects

```javascript
var topUserPostsRef = firebase
  .database()
  .ref('user-posts/' + myUserId)
  .orderByChild('starCount');

var mostViewedPosts = firebase
  .database()
  .ref('posts')
  .orderByChild('metrics/views');
```

### Filter data

> Assumes that data is ordered by key unless otherwise specified

* **limitToFirst(count)**: Sets the maximum number of items to return from the beginning of the ordered list of results.
* **limitToLast(count)**: Sets the maximum number of items to return from the end of the ordered list of results.
* **startAt(value)**: Return items greater than or equal to the specified key or value, depending on the order-by method chosen.
* **endAt(value)**: Return items less than or equal to the specified key or value, depending on the order-by method chosen.
* **equalTo(value)**: Return items equal to the specified key or value, depending on the order-by method chosen.

```javascript
var first100Days = firebase
  .database()
  .ref('days/2018')
  .orderByChild('dayOfYear')
  .limitToFirst(100);

var first10DaysOfFebruary = firebase
  .database()
  .ref('days/2018')
  .orderByChild('dayOfYear')
  .limitToFirst(10)
  .startAt(32);

var last10DaysOfJanuary = firebase
  .database()
  .ref('days/2018')
  .orderByChild('dayOfYear')
  .limitToLast(10)
  .endAt(31);

var first10DaysOfJanuary = firebase
  .database()
  .ref('days/2018')
  .orderByChild('dayOfYear')
  .limitToFirst(100) // Limit is never hit
  .endAt(10); // endAt stops the query before it hits the limit
```

### Authenticate Node.js

> Full admin privileges

```javascript
var admin = require('firebase-admin');

// Fetch the service account key JSON file contents
var serviceAccount = require('path/to/serviceAccountKey.json');

// Initialize the app with a service account, granting admin privileges
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://databaseName.firebaseio.com',
});

// As an admin, the app has access to read and write all data, regardless of Security Rules
var db = admin.database();
var ref = db.ref('restricted_access/secret_document');
ref.once('value', function(snapshot) {
  console.log(snapshot.val());
});
```

### Initialize Node.js with limited privileges

> Set auth token variables to limit access

```javascript
// Initialize the app with a custom auth variable, limiting the server's access
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://databaseName.firebaseio.com',
  databaseAuthVariableOverride: {
    uid: 'my-service-worker',
  },
});
```

> Act as an un-authenticated user

```javascript
// Initialize the app with a custom auth variable, limiting the server's access
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://databaseName.firebaseio.com',
  databaseAuthVariableOverride: null,
});
```


# Introduction

Cloud Functions is the connective tissue that makes Firebase a "serverless" platform.

Secure operations are unavoidable in production applications. Sure, you can make a quick demo that doesn't need to send any email or accept a Stripe payment... but most apps are worthless without secure, administrative operations.

Until the advent of Functions-as-a-Service (aka FaaS), we had to run servers to perform administrative actions on the Firebase platform.

Cloud Functions lets us ship single functions to the Firebase platform for execution based on a whole variety of events.

## Serverless: other people's servers

Cloud Functions runs on servers, but you don't get to interact with those servers. Cloud Functions has your code and will run it as Cloud Functions sees fit. It can spin up 20 servers to handle a spike in traffic, and it can spin them all down again without any input from you or your code.

## What does a function look like?

Here's a basic "hello world" function that completes some asynchronous work and returns a promise.

```javascript
// Always change the value of "/hello" to "world!"
exports.hello = functions.database.ref('/hello').onWrite(event => {
  // set() returns a promise. We keep the function alive by returning it.
  return event.data.ref.set('world!').then(() => {
    console.log('Write succeeded!');
  });
});
```

This function is named "hello" and it listens to an `onWrite` event on the `/hello` ref. This function will execute any time any data changes on the `/hello` ref, including writes, updates and deletes.

## Event sources

Cloud Functions is a Google Cloud Platform (GCP) service, but it has a series of Firebase-specific events, so we often refer to those events as Cloud Functions for Firebase.

Cloud Functions is for all of GCP, not just Firebase... but it's especially useful when combined with Firebase.

Here's a quick list of some Cloud Functions event sources:

* HTTP
* Firebase Realtime Database
* Firestore
* Firebase Authentication
* Firebase Storage

Let's dive in a little deeper to each event source.

## HTTP triggers

Cloud Functions enables us to use the Express framework for Node.js to handle HTTP calls.

We can mount an entire Express app to a Cloud Functions endpoint, or we can attach a single Express request handler to each Cloud Function endpoint. It's quite flexible and enables us to create any kind of HTTP-based API that we could ever need.

## Firebase Realtime Database triggers

We can listen to any part of our RTDB JSON tree with the following triggers:

* onWrite()
* onCreate()
* onUpdate()
* onDelete()

The events do exactly what you'd think. The one catch is that `onWrite()` executes for any and all changes to the data tree including deletions... so use it wisely :)

RTDB triggers are great for creating job queues. For instance, you can queue up a bunch of jobs under `/job-queues/image-resizing/` and write a Cloud Function that resizes images. Crazy, right? You can create 100 image resizing jobs that Cloud Functions will execute as fast as it can, often running batches of jobs in parallel.

## Firestore triggers

Firestore supports the same Cloud Functions triggers as the RTDB:

* onWrite()
* onCreate()
* onUpdate()
* onDelete()

And once again, `onWrite()` is triggered for any and all changes including deletes.

Firestore triggers are great for manipulating data. For instance, if we're building a Slack messaging clone and we want to automatically subscribe new users to the most popular channels, we can listen to Firestore for new User objects, and every time a new User object gets written we can query the top ten most popular channels and add them to the new user's channels list.

## Firebase Authentication triggers

Authentication has only two triggers:

* onCreate()
* onDelete()

We like to use the `onCreate()` event to set custom attributes on our user's auth tokens. This method is known as [custom claims](https://firebase.google.com/docs/auth/admin/custom-claims), and it's extremely helpful for granting elevated privileges in Firebase, Firestore and Storage security rules.

```javascript
const admin = require('firebase-admin');
const serviceAccount = require('path/to/serviceAccountKey.json');
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://databaseName.firebaseio.com',
});

const emails = new Set(['chris@fullstackfirebase.com', 'juarez@howtofirebase.com']);

exports.setCustomClaims = functions.auth.user().onCreate(event => {
  let promise = Promise.resolve();
  if (emails.has(user.email) {
    promise = admin
      .auth()
      .setCustomUserClaims(user.uid, { admin: true })
      .then(() => true);
  }

  return promise;
});
```

## Firebase Storage triggers

Firebase Storage has a single trigger with two ways to listen to it:

> The optional `.bucket('bucketName')` function allows for filtering based on storage buckets

```javascript
exports.generateThumbnail = functions.storage.object().onChange(event => {
  // ...
});

exports.generateThumbnail = functions.storage
  .bucket('bucketName')
  .object()
  .onChange(event => {
    // ...
  });
```

Generating thumbnails or other kinds of image manipulation is a core use case for Firebase Storage triggers. Each Cloud Functions virtual machine has a [native build of Imagemagick](https://cloud.google.com/functions/docs/tutorials/imagemagick) installed for this very purpose.

Just note that Firebase Storage is backed by Google Cloud Storage, which treats each bucket as a single store.

Cloud Storage lets you put slashes in filenames, which is how Firebase Storage simulates folders... but it's just one big bucket under the hood. This is why you can only listen to Cloud Storage events at the bucket level. You'll need to do your own filtering based on the filenames of your objects.

## So many possibilities

Cloud Functions enables you to get truly creative in how you architect your serverless apps.

You can craft highly scalable job pipelines, complex messaging systems, or simply track and tweak your data as your users make changes to your databases.

Start out with simple functions. Don't get carried away until you're comfortable with the basics. Cloud Functions is an ocean; wade around in the shallows before heading out to deep water.

And we're still discovering new application architectures that Functions-as-a-service makes possible. These are early days for all of us.


# Walkthrough

## Open the app

Open the app at <https://glitch.com/edit/#!/wind-shrimp>.

## Video

{% embed url="<https://youtu.be/PxwdciybqyU>" %}


# Challenge

## Cloud Functions

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [challenge-functions](https://github.com/how-to-firebase/firelist-react/tree/challenge-functions).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout challenge-functions
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`
* `/functions/environments/environment.dev.js`
* `/functions/environments/environment.js`

## Run the tests

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then `cd` into your `functions` directory and run `yarn` or `npm install` again.

Make sure that you've installed [nvm for Linux/macOS](https://github.com/creationix/nvm), [nvm for Windows](https://github.com/coreybutler/nvm-windows) or some other tool to switch between Node.js versions.

Use `nvm install 6.14.2` then `nvm use 6.14.2` to switch to Node version 6.14.2.

The Node version is important to make sure that your tests run in an identical environment to that of the Cloud Functions runtime.

Then run `yarn test` or `npm test` to run your tests.

```
yarn
cd functions
nvm install 6.14.2
nvm use 6.14.2
node --version //should read out 6.14.2
yarn test
```

## Complete the challenge

Search the codebase for `Challenge Functions` to find all of the challenges.

Work through the challenges in order from 01 to 06


# Notes

## Cloud Functions

See the [Cloud Functions docs for Firebase](https://firebase.google.com/docs/functions/get-started).

#### Functions samples

See [the official GitHub repo of Cloud Functions for Firebase sample functions](https://github.com/firebase/functions-samples)

### Mount an Express app

```javascript
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const app = express();

// Automatically allow cross-origin requests
app.use(cors({ origin: true }));

// Add middleware to authenticate requests
app.use(myMiddleware);

// build multiple CRUD interfaces:
app.get('/:id', (req, res) => res.send(Widgets.getById(req.params.id)));
app.post('/', (req, res) => res.send(Widgets.create()));
app.put('/:id', (req, res) => res.send(Widgets.update(req.params.id, req.body)));
app.delete('/:id', (req, res) => res.send(Widgets.delete(req.params.id)));
app.get('/', (req, res) => res.send(Widgets.list()));

// Expose Express API as a single Cloud Function:
exports.widgets = functions.https.onRequest(app);
```

### Mount an Express handler

```javascript
exports.helloWorld = functions.https.onRequest((req, res) => {
  res.status(200);
  res.send('hello world');
});
```

### Firestore triggers

* onCreate
* onUpdate
* onDelete
* onWrite

```javascript
exports.createUser = functions.firestore.document('users/{userId}').onCreate(event => {
  // Get an object representing the document
  // e.g. {'name': 'Marie', 'age': 66}
  var newValue = event.data.data();

  // access a particular field as you would any JS property
  var name = newValue.name;

  // perform desired operations ...
});
```

### Realtime Database triggers

* onCreate
* onUpdate
* onDelete
* onWrite

```javascript
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original').onWrite(event => {
  // Grab the current value of what was written to the Realtime Database.
  const original = event.data.val();
  console.log('Uppercasing', event.params.pushId, original);
  const uppercase = original.toUpperCase();
  // You must return a Promise when performing asynchronous tasks inside a Functions such as
  // writing to the Firebase Realtime Database.
  // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
  return event.data.ref.parent.child('uppercase').set(uppercase);
});
```

### Firebase Authentication

* onCreate
* onDelete

```javascript
exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
  const user = event.data; // The Firebase user.

  const email = user.email; // The email of the user.
  const displayName = user.displayName; // The display name of the user.
});
```

### Firebase Storage

* onChange

```javascript
exports.generateThumbnail = functions.storage.object().onChange(event => {
  const object = event.data; // The Storage object.

  const fileBucket = object.bucket; // The Storage bucket that contains the file.
  const filePath = object.name; // File path in the bucket.
  const contentType = object.contentType; // File content type.
  const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions).
  const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.

  // Exit if this is triggered on a file that is not an image.
  if (!contentType.startsWith('image/')) {
    console.log('This is not an image.');
    return;
  }

  // Get the file name.
  const fileName = path.basename(filePath);
  // Exit if the image is already a thumbnail.
  if (fileName.startsWith('thumb_')) {
    console.log('Already a Thumbnail.');
    return;
  }

  // Exit if this is a move or deletion event.
  if (resourceState === 'not_exists') {
    console.log('This is a deletion event.');
    return;
  }

  // Exit if file exists but is not new and is only being triggered
  // because of a metadata change.
  if (resourceState === 'exists' && metageneration > 1) {
    console.log('This is a metadata change event.');
    return;
  }
});
```

### Use ImageMagick

```javascript
const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');

exports.generateThumbnail = functions.storage.object().onChange(event => {
  const object = event.data;

  const fileBucket = object.bucket;
  const filePath = object.name;
  const contentType = object.contentType;

  // Download file from bucket.
  const bucket = gcs.bucket(fileBucket);
  const tempFilePath = path.join(os.tmpdir(), fileName);
  const metadata = { contentType: contentType };
  return bucket
    .file(filePath)
    .download({
      destination: tempFilePath,
    })
    .then(() => {
      console.log('Image downloaded locally to', tempFilePath);
      // Generate a thumbnail using ImageMagick.
      return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', tempFilePath]);
    })
    .then(() => {
      console.log('Thumbnail created at', tempFilePath);
      // We add a 'thumb_' prefix to thumbnails file name. That's where we'll upload the thumbnail.
      const thumbFileName = `thumb_${fileName}`;
      const thumbFilePath = path.join(path.dirname(filePath), thumbFileName);
      // Uploading the thumbnail.
      return bucket.upload(tempFilePath, { destination: thumbFilePath, metadata: metadata });
      // Once the thumbnail has been uploaded delete the local file to free up disk space.
    })
    .then(() => fs.unlinkSync(tempFilePath));
});
```


# Introduction

Every image that you upload online has to get stored somewhere, and cloud storage providers such as Amazon and Google Cloud will store those files as "objects" in "buckets".

The Firebase Realtime Database and Cloud Firestore are great for storing data, but they're not so good with files. Google Cloud Storage, heretofore referred to as Cloud Storage, is built to store and serve these files.

Firebase Storage is a front for Cloud Storage... an extremely useful front.

## The old file-upload pattern

Browsers are great at uploading files, but files are often too big to send over a single HTTP POST, so they're typically streamed to a server. This streaming happens as a series of chunks which the server has to listen for, waiting patiently until the total size of the chunks adds up to the expected file size. The server can then take the file that it has assembled from a bunch of chunks and stream it up to Cloud Storage... again, in a series of chunks.

We've written plenty of file streaming servers, and they're a pain in the neck.

## Firebase Storage saves the day

The old file-upload pattern requires a sophisticated server. And Firebase is all about getting rid of your servers.

Firebase Storage enables you to upload files directly from a browser to Cloud Storage. Firebase Storage handles all of the servers and all of the streaming, leaving you with a simple JavaScript interface.

It seems like a small feature.

Firebase Storage is tiny.

All it does is upload files.

But it's hiding a ton of complexity that you'd have to code yourself, so it's a massive win.

## It's just a bucket

Cloud Storage treats each bucket as just that, a bucket.

Cloud Storage does not have a concept of folders.

But you **can** put forward slashes in your filenames, which Firebase Storage will treat as a file path.

For example, we've created a Firebase project named `Quiver Four`; therefore, Firebase Storage automatically creates a Cloud Storage bucket named `quiver-four.appspot.com`.

Let's upload a file to `howtofirebase/uploads/enable-firestore.png`:

```javascript
function uploadFile(file) {
  return firebase
    .storage()
    .ref()
    .child('howtofirebase/uploads/enable-firestore.png')
    .put(file)
    .then(snapshot => {
      // snapshot represents the uploaded file
    });
}
```

And the Firebase Console pretends that the file is in a nested folder!

![storage-browser.png](https://goo.gl/r5bWP9)

Cloud Storage pretends that it's in a folder as well:

![cloud-storage-browser.png](https://goo.gl/mVB1p8)

But then we pulled the file details down using the Cloud Storage SDK and pushed them up to Firestore. Notice that file's name attribute is `howtofirebase/uploads/enable-firestore.png`.

![file-details.png](https://goo.gl/fhm5w5)

## Cloud Storage SDK

Firebase Storage does not have it's own Node.js SDK. It's a browser-only system.

But do not despair! Since these files are merely Cloud Storage objects in a Cloud Storage bucket, we can use the Cloud Storage SDK to interact with them in Node.js.

And we can get Cloud Storage SDK bucket references straight from the Firebase Admin SDK in Node.js!

```javascript
const admin = require("firebase-admin");

const serviceAccount = require("path/to/serviceAccountKey.json");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    storageBucket: "quiver-foure.appspot.com"
});

const bucket = admin.storage().bucket();
// 'bucket' is a Cloud Storage bucket instance
```

`bucket` is an object defined by the [@google-cloud/storage library](https://cloud.google.com/nodejs/docs/reference/storage/1.5.x/Bucket) for Node.js. The GCP libraries feel different from the Firebase libraries, mostly because the docs look different. But don't be afraid of GCP. It gives you much finer-grained control over its features than Firebase does.


# Walkthrough

## Open the app

Open the app at [fogo.howtofirebase.com](https://fogo.howtofirebase.com/).

## Open DevTools

[Sourcemaps are currently broken in Chrome](https://github.com/webpack/webpack/issues/3165) for Fogo as of July 2018.

This is best done in Firefox.

Right-click `inspect element` and select the `Debugger` tab. Then click `command + P` to search for and open one file:

* `storage-uploader/src/services/storage.service.js`.

![storage-uploader/src/services/storage.service.js](https://goo.gl/ebT6o7)

## Video

{% embed url="<https://youtu.be/cuHTeqwbr44>" %}


# Security Rules

## Security Rules

## Firebase Storage

Firebase Storage exposes quite a bit of functionality to the public, so you'll need to write some security rules.

Like Firestore, Firebase Storage uses Firebase's new security rules syntax.

### Review Firestore security rules

The best way to understand Firebase Storage security rules is to read up on [Firestore security rules](https://how-to-firebase.gitbooks.io/full-stack-firebase/content/cloud-firestore/security-rules.html). They're basically the same.

### The basics

The basic rules look something like this:

```
// Only authenticated users can read or write to the bucket
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
```

Let's break these rules down line-by-line.

**service firebase.storage** - defines the service, in this case it's firebase.storage

**match /b/{bucket}/o** - defines the bucket; the `{bucket}` clause indicates that these rules apply to all Cloud Storage buckets on the project

**match /{allPaths=\*\*}** - creates a new rules block to apply to all paths

**allow read, write: if requests.auth != true ;** - allows read/write access for all authenticated sessions

### Match blocks

Match blocks are identical to [those of Firestore](https://how-to-firebase.gitbooks.io/full-stack-firebase/content/cloud-firestore/security-rules.html).

Of course, instead of matching collections and documents, you're matching folders and storage objects. But that's the only difference.

Let's write a match block for a folder structure like this: `/user/{userId}/path/to/file.txt`

```
service firebase.storage {
  match /b/{bucket}/o {
    match /user/{userId}/{allPaths=**} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}
```

And, like Firestore, match blocks can be nested... if you need it.

Firebase Storage security rules tend to be a bit simpler than Firestore's.

### Rule types

Firebase Storage supports `read` and `write`. That's it. This is a break from Cloud Firestore which supports other sub-types. In this case you're either reading or writing.

### Wildcard variables

Wildcards work just like those in Firestore. You can place them at will and override them as needed. You'll also want to be careful to use the `{someWildcard=**}` syntax when you want your rules to cascade; otherwise, they won't apply to nested folders.

The following example secures a dropbox-style pattern where users can upload to an uploads folder at `/user/uploads/{userId}/uploaded-file.jpg` but can only read from `/user/thumbnails/{userId}/thumbnail.jpg`.

```
service firebase.storage {
  match /b/{bucket}/o {
    match /user/ {
      match /thumbnails/{userId}/{thumbnail} {
        allow read: if request.auth.uid == userId;
      }
      match /uploads/{userId}/{upload} {
        allow write: if request.auth.uid == userId;
      }
    }
  }
}
```

### Request variables

Rule conditions have access to a `request` object that represents that incoming request. You'll be using the `request` object for most rule conditions.

The fields of most interest are `request.auth.uid` and `request.auth.token`, which contains the user's JWT.

### Resource variables

Rule conditions also have access to a `resource` object. In Firestore this object refers to the pre-write state of the document, but in Firebase Storage this is the object being uploaded, downloaded, modified or deleted.

Here's a sample `resource` object that you may find handy:

```javascript
{
  "name": "howtofirebase/uploads/locked-mode.png",
  "bucket": "quiver-four.appspot.com",
  "generation": "1517664154693678",
  "metageneration": "1",
  "size": "138099",
  "timeCreated": "2018-02-03T13:22:34.676Z",
  "updated": "2018-02-03T13:22:34.676Z",
  "md5Hash": "Yzg3ZGNlZDVjODZiYzAyNGU4NTljYTU2MDdlZDMwMjk=",
  "crc32c": "QK5Kyw==",
  "etag": "CK7g0MbridkCEAE=",
  "contentDisposition": "inline; filename*=utf-8''locked-mode.png",
  "contentEncoding": "gzip",
  "contentLanguage": "en",
  "contentType": "image/png",
  "metadata": {
    "firebaseStorageDownloadTokens": "c7dfc4c3-0d91-4e1a-b6a1-d4e03a320ef1"
  }
}
```

### Read the docs

It's been said before and we'll say it again. Read the docs.

The highlights:

* [the Request object](https://firebase.google.com/docs/reference/security/storage/#request)
* [the Resource object](https://firebase.google.com/docs/reference/security/storage/#resource)
* [Data types](https://firebase.google.com/docs/reference/security/storage/#data_types)


# Challenge

## Firebase Storage

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [challenge-storage](https://github.com/how-to-firebase/firelist-react/tree/challenge-storage).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout challenge-storage
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`
* `/functions/environments/environment.dev.js`
* `/functions/environments/environment.js`

## Deploy Cloud Functions

If you had some trouble deploying Cloud Functions earlier, make sure to deploy the correct functions now.

Run `yarn deploy:functions` to deploy valid versions of each function up to your Firebase project.

## Start the app

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then run `yarn start` or `npm run start` to spin up the development server.

```
yarn
yarn start
```

## Complete the challenge

Search the codebase for `Challenge Storage` to find all of the challenges.

Read the comments and complete the steps in those files.

* `src/storage/delete-image.js`
* `src/storage/get-upload-observable.js`


# Notes

## Firebase Storage

See the [Firebase Storage docs for web](https://firebase.google.com/docs/storage/web/start).

### Create a ref

```javascript
var storageRef = firebase.storage().ref();
const fileRef = storageRef.child('/some/file/path.jpg);
```

### Navigate

```javascript
// Points to the root reference
var storageRef = firebase.storage().ref();

// Points to 'images'
var imagesRef = storageRef.child('images');

// Points to 'images/space.jpg'
// Note that you can use variables to create child values
var fileName = 'space.jpg';
var spaceRef = imagesRef.child(fileName);

// File path is 'images/space.jpg'
var path = spaceRef.fullPath;

// File name is 'space.jpg'
var name = spaceRef.name;

// Points to 'images'
var imagesRef = spaceRef.parent;
```

### Upload file

```javascript
// Create file metadata including the content type
var metadata = {
  contentType: 'image/jpeg',
};

// Upload the file and metadata
var uploadTask = storageRef.child('images/mountains.jpg').put(file, metadata);
```

### Full example

```javascript
function uploadFile(file) {
  // Create the file metadata
  var metadata = {
    contentType: 'image/jpeg',
  };

  // Upload file and metadata to the object 'images/mountains.jpg'
  var uploadTask = storageRef.child('images/' + file.name).put(file, metadata);

  // Listen for state changes, errors, and completion of the upload.
  uploadTask.on(
    firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
    function(snapshot) {
      // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
      var progress = snapshot.bytesTransferred / snapshot.totalBytes * 100;
      console.log('Upload is ' + progress + '% done');
      switch (snapshot.state) {
        case firebase.storage.TaskState.PAUSED: // or 'paused'
          console.log('Upload is paused');
          break;
        case firebase.storage.TaskState.RUNNING: // or 'running'
          console.log('Upload is running');
          break;
      }
    },
    function(error) {
      // Errors list: https://firebase.google.com/docs/storage/web/handle-errors
      switch (error.code) {
        case 'storage/unauthorized':
          // User doesn't have permission to access the object
          break;

        case 'storage/canceled':
          // User canceled the upload
          break;

        case 'storage/unknown':
          // Unknown error occurred, inspect error.serverResponse
          break;
      }
    },
    function() {
      // Upload completed successfully, now we can get the download URL
      var downloadURL = uploadTask.snapshot.downloadURL;
    }
  );
}
```

### Download file

```javascript
// Create a reference to the file we want to download
var starsRef = storageRef.child('images/stars.jpg');

// Get the download URL
starsRef.getDownloadURL().then(function(url) {
  // Insert url into an <img> tag to "download"
}).catch(function(error) {

  // A full list of error codes is available at
  // https://firebase.google.com/docs/storage/web/handle-errors
  switch (error.code) {
    case 'storage/object_not_found':
      // File doesn't exist
      break;

    case 'storage/unauthorized':
      // User doesn't have permission to access the object
      break;

    case 'storage/canceled':
      // User canceled the upload
      break;

    ...

    case 'storage/unknown':
      // Unknown error occurred, inspect the server response
      break;
  }
});
```

### Set metadata

```javascript
// Create a reference to the file whose metadata we want to change
var forestRef = storageRef.child('images/forest.jpg');

// Create file metadata to update
var newMetadata = {
  cacheControl: 'public,max-age=300',
  contentType: 'image/jpeg',
  contentLanguage: null,
  customMetadata: {
    whatever: 'we feel like',
  },
};

// Update metadata properties
forestRef
  .updateMetadata(newMetadata)
  .then(function(metadata) {
    // Updated metadata for 'images/forest.jpg' is returned in the Promise
  })
  .catch(function(error) {
    // Uh-oh, an error occurred!
  });
```


# Introduction

Cloud Messaging is used for those notifications that pop up in the corner of your Chrome or Firefox browser.

They're also used to pop native Android and iOS notifications.

Firebase Cloud Messaging (FCM) used to be known as Google Cloud Messaging (GCM), and if you're familiar with GCM... well, not much has changed.

FCM is advertised primarily for use with Android and iOS. The Firebase Console doesn't even have an FCM page on it. There's a Notifications page for Android and iOS that encapsulates FCM functionality, but it's useless for web.

We'll be interacting directly with the FCM API using a Cloud Function. No Console page is required.

## How do I send messages?

FCM is actually one of the simpler Firebase modules.

You send messages using Cloud Functions or your own server, and you receive them with a service worker in your browser.

You can message individual browsers, and you can subscribe a browser to a "topic" so that it can receive bulk messages.

We'll get into the details later... just know that there's not much to it. You can send messages to your clients either individually or by topic. And the device handles displaying the message. Your work is done.

## Why send messages?

Excessive browser and mobile notifications are obnoxious. Our phones and browsers light up constantly with Twitter notifications trying to drag us back into their app. We disable those notifications pretty aggressively.

But there's a reason for the flood of alerts. They work. They pull people back into Twitter, Tumblr, and Facebook. And they're super helpful when you miss a bunch of Slack messages or emails from the office.

You're likely already sending notifications if you develop for Android or iOS. We won't be covering that here, but we'll show you how to send notifications to a web browser. Again, it's not complicated.


# Walkthrough

## Open the app

Open the app at <https://fine-ping.glitch.me/>.

## Open DevTools

Right-click `inspect element` and select the `Debugger` tab. Then click `command + P` to search for and open two files:

* `client.js`,
* and `firebase-messaging-sw.js`.

You'll likely need to open the `Application` tab in DevTools and navigate to the `Service Workers` side tab. Then click the link for `firebase-messaging-sw.js` to inspect it in the `Sources` tab.

![client.js](https://goo.gl/3EmcES)

![firebase-messaging-sw.js](https://goo.gl/xoGxvx)

## Video

{% embed url="<https://youtu.be/QaTNDARGH0E>" %}


# Challenge

## Cloud Messaging

## Find the repo

We'll be working on a branch of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo named [firebase-messaging](https://github.com/how-to-firebase/firelist-react/tree/master).

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout firebase-messaging
```

Once you're on the branch, make sure to run `yarn` or `npm install` to get your Node.js dependencies.

Then run `yarn start` or `npm run start` to spin up the development server.

```
yarn
yarn start
```

## Complete the challenge

We'll be editing `./src/components/messaging.js` and `./public/sw.js`.

Read the comments and complete the steps in those two files.

## Notes on sw\.js vs firebase-messaging-sw\.js

Firebase Messaging recommends calling your file [firebase-messaging-sw.js](https://firebase.google.com/docs/cloud-messaging/js/receive).

This is fine as long as you don't care about running your app as a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) or PWA.

However! We believe that all apps should be PWAs, so we've configured this project to use the standard `sw.js` filename.

You'll notice a function in `./src/components/messaging.js` named `registerServiceWorker`

```
async function registerServiceWorker(messaging) {
  if ('serviceWorker' in navigator) {
    const registration = await navigator.serviceWorker.register('/sw.js');
    messaging.useServiceWorker(registration);
  }
}
```

This function is will register `/sw.js` and then, once that's done, tell messaging to use `/sw.js` instead of `firebase-messaging-sw.js`.

You can only have one service worker per page. This doesn't work any other way. Trust us. We've tried.

Yes, you can add all of the service worker functionality that you like into `firebase-messaging-sw.js` and let Firebase Messaging register that file automatically.

However, this automatic registration will not allow your users to install your app to their homescreen as a PWA... which totally defeats the purpose of a PWA.

Hence our insistence on using `/sw.js`.


# Notes

## Cloud Messaging

See the [Firebase Cloud Messaging docs for web](https://firebase.google.com/docs/cloud-messaging/js/client).

### manifest.json

```javascript
{
  "gcm_sender_id": "103953800507"
}
```

### Request permission in browser

```javascript
// index.html
const messaging = firebase.messaging();
messaging
  .requestPermission()
  .then(function() {
    // Get Instance ID token. Initially this makes a network call, once retrieved
    // subsequent calls to getToken will return from cache.
    messaging.getToken()
    .then(function(currentToken) {
      if (currentToken) {
        sendTokenToServer(currentToken);
        updateUIForPushEnabled(currentToken);
      } else {
        // Show permission request.
        console.log('No Instance ID token available. Request permission to generate one.');
        // Show permission UI.
        updateUIForPushPermissionRequired();
        setTokenSentToServer(false);
      }
    })
    .catch(function(err) {
      console.log('An error occurred while retrieving token. ', err);
      showToken('Error retrieving Instance ID token. ', err);
      setTokenSentToServer(false);
    });
  }
  .catch(function(err) {
    console.log('Unable to get permission to notify.', err);
  });
```

### Monitor token refresh

```javascript
// index.html
// Callback fired if Instance ID token is updated.
messaging.onTokenRefresh(function() {
  messaging
    .getToken()
    .then(function(refreshedToken) {
      console.log('Token refreshed.');
      // Indicate that the new Instance ID token has not yet been sent to the
      // app server.
      setTokenSentToServer(false);
      // Send Instance ID token to app server.
      sendTokenToServer(refreshedToken);
      // ...
    })
    .catch(function(err) {
      console.log('Unable to retrieve refreshed token ', err);
      showToken('Unable to retrieve refreshed token ', err);
    });
});
```

### Catch messages when page is in foreground

```javascript
// index.html
// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a sevice worker
//   `messaging.setBackgroundMessageHandler` handler.
messaging.onMessage(function(payload) {
  console.log('Message received. ', payload);
  // ...
});
```

### Create serviceWorker

> You need a serviceWorker to listen for messages in the background

```javascript
// firebase-messaging-sw.js
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  messagingSenderId: 'YOUR-SENDER-ID',
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
```

### Send message to single recipient

```javascript
// Cloud Function
// This registration token comes from the client FCM SDKs.
var registrationToken = 'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...';

// See the "Defining the message payload" section below for details
// on how to define a message payload.
var payload = {
  notification: {
    title: 'Title of your push notification',
    body: 'Body of your push notification',
    click_action: 'https://dummypage.com',
  },
  data: {
    score: '850',
    time: '2:45',
  },
};

// Send a message to the device corresponding to the provided
// registration token.
admin
  .messaging()
  .sendToDevice(registrationToken, payload)
  .then(function(response) {
    // See the MessagingDevicesResponse reference documentation for
    // the contents of response.
    console.log('Successfully sent message:', response);
  })
  .catch(function(error) {
    console.log('Error sending message:', error);
  });
```

### Send multi-cast message

```javascript
// Cloud Function
// These registration tokens come from the client FCM SDKs.
var registrationTokens = [
  'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...',
  // ...
  'ecupwIfBy1w:APA91bFtuMY7MktgxA3Au_Qx7cKqnf...',
];

//...
admin
  .messaging()
  .sendToDevice(registrationTokens, payload)
  .then(function(response) {
    //...
  });
```

### Send device-group message

> See [managing device groups](https://firebase.google.com/docs/cloud-messaging/android/device-group#managing_device_groups)

```javascript
// Cloud Function
var notificationKey = 'some-notification-key';

//...
admin
  .messaging()
  .sendToDeviceGroup(notificationKey, payload)
  .then(function(response) {
    // ...
  });
```

### Send topic message

> See [managing device groups](https://firebase.google.com/docs/cloud-messaging/android/device-group#managing_device_groups)

```javascript
// Cloud Function
// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';

//...

admin
  .messaging()
  .sendToTopic(topic, payload)
  .then(function(response) {
    //...
  });
```

### Send to condition

> Conditions support only two operations per expression

```javascript
// Cloud Function
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

//...
admin
  .messaging()
  .sendToCondition(condition, payload)
  .then(function(response) {
    //...
  });
```

### Message options

```javascript
// Cloud Function
var registrationToken = 'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...';

var payload = {
  notification: {
    title: 'Urgent action needed!',
    body: 'Urgent action is needed to prevent your account from being disabled!',
  },
};

// Set the message as high priority and have it expire after 24 hours.
var options = {
  priority: 'high',
  timeToLive: 60 * 60 * 24,
};

admin
  .messaging()
  .sendToDevice(registrationToken, payload, options)
  .then(function(response) {
    //...
  });
```

### Subscribe to topic

```javascript
// Cloud Function
var registrationTokens = [
  'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...',
  // ...
  'ecupwIfBy1w:APA91bFtuMY7MktgxA3Au_Qx7cKqnf...',
];

admin
  .messaging()
  .subscribeToTopic(registrationTokens, topic)
  .then(function(response) {
    //...
  });
```

### Subscribe to topic

```javascript
// Cloud Function
const registrationTokens = [
  'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...',
  // ...
  'ecupwIfBy1w:APA91bFtuMY7MktgxA3Au_Qx7cKqnf...',
];

const topic = 'highScores';

admin
  .messaging()
  .subscribeToTopic(registrationTokens, topic)
  .then(function(response) {
    //...
  });
```

### Unsubscribe to topic

```javascript
// Cloud Function
const registrationTokens = [
  'bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...',
  // ...
  'ecupwIfBy1w:APA91bFtuMY7MktgxA3Au_Qx7cKqnf...',
];

const topic = 'highScores';

admin
  .messaging()
  .unsubscribeFromTopic(registrationTokens, topic)
  .then(function(response) {
    //...
  });
```


# Introduction

Every web app needs to serve static content. Static content includes your HTML, JavaScript and CSS files as well as any images or other assets that your client-side app needs to run.

There are plenty of easy ways to serve up static files; but since every app needs to do it, Firebase has an integrated static file hosting solution.

## Advantages of Firebase Hosting

Firebase Hosting is fully integrated with the rest of the Firebase platform. You can deploy easily from the command line. You can see those deploys on the Firebase Console and roll back to earlier versions if you break something.

Hosting supports URL redirects, URL rewrites, control over headers and default HTTP2 support.

You can easily connect a custom domain, and Firebase Hosting will automatically provision your SSL certificate, so you will always be protected by HTTPS.

And the icing on the cake is Firebase Hosting's integration with Cloud Functions. You can, with a couple of lines of configuration, redirect a URL on your Hosting domain to any of your HTTP-triggered Cloud Functions. Now you can serve up dynamic content in addition to your static files!

## Disadvantages of Firebase Hosting

There aren't any. The headline is misleading.


# Walkthrough

## Open the app

Open the app at <https://glitch.com/edit/#!/ripe-knife>.

## Video

{% embed url="<https://youtu.be/SX5kh7J1LsE>" %}


# Challenge

## Firebase Hosting

## Find the repo

We'll be working on the [master branch](https://github.com/how-to-firebase/firelist-react) of our [firelist-react](https://github.com/how-to-firebase/firelist-react) repo.

## Localhost installation

Pull [the repo](https://github.com/how-to-firebase/firelist-react) directly from GitHub...

```
git clone https://github.com/how-to-firebase/firelist-react.git
cd firelist-react
git checkout master
```

## Edit environment files

Update the following files with your own project details:

* `/.firebaserc`
* `/functions/environments/environment.dev.js`
* `/functions/environments/environment.js`
* `/public/environments/environment.dev.js`
* `/public/environments/environment.js`

## Build the app

Once you're on the branch, make sure to run `yarn build` or `npm install` to get your Node.js dependencies.

Build the app with `yarn build` or `yarn build:windows`.

## Inspect firebase.json

Read through `/firebase.json` and try to understand the settings.

## Deploy the app

Just run `yarn deploy`!

Check out `/package.json` and the `scripts` attribute to see what scripts are available and what they do.


# Notes

## Firebase Hosting

See the [Firebase Hosting doc for web](https://firebase.google.com/docs/cloud-messaging/js/client).

### Redirects

```javascript
"hosting": {
  // Add the "redirects" section within "hosting"
  "redirects": [ {
    "source" : "/foo",
    "destination" : "/bar",
    "type" : 301
  }, {
    "source" : "/firebase/*",
    "destination" : "https://firebase.google.com",
    "type" : 302
  } ]
}
```

### Rewrites

```javascript
"hosting": {
  // Add the "rewrites" section within "hosting"
  "rewrites": [ {
    "source": "**",
    "destination": "/index.html"
  } ]
}
```

### Headers

```javascript
"hosting": {
    // Add the "headers" section within "hosting".
    "headers": [ {
      "source" : "**/*.@(eot|otf|ttf|ttc|woff|font.css)",
      "headers" : [ {
        "key" : "Access-Control-Allow-Origin",
        "value" : "*"
    } ]
    }, {
      "source" : "**/*.@(jpg|jpeg|gif|png)",
      "headers" : [ {
      "key" : "Cache-Control",
      "value" : "max-age=7200"
      } ]
    }, {
      // Sets the cache header for 404 pages to cache for 5 minutes
      "source" : "404.html",
      "headers" : [ {
      "key" : "Cache-Control",
      "value" : "max-age=300"
      } ]
    } ]
 }
```

### Connect a Cloud Function

```javascript
{
  "hosting": {
    "public": "public",

    // Add the following rewrites section *within* "hosting"
   "rewrites": [ {
      "source": "/bigben", "function": "bigben"
    } ]

  }
}
```


