This document covers the overall architecture and design decisions made for this base template, and the associated FullStacksDev Angular and Firebase tech stack.
Note
Feel free to deviate from these as you wish. The base template is designed to be fairly flexible so you can adapt it to your needs. Though note that deviations may require more tinkering to get everything to work well together.
If you're keen on the full curated tech stack experience — where we build on top of this base template with more opinionated patterns, practices and approaches — then check out the example apps.
Tip
If you're reading this on GitHub's web UI you can view a table of contents by clicking the relevant button in the top right corner of the document.
To make the information skimmable and easier to understand, you'll see the following standout blocks throughout:
🧠 Design decision |
---|
A decision that was made and why. Can cover any aspect: technical, architectural, design, UX, etc. |
✅ Pattern |
---|
A recommended way of doing something. This term is used loosely and non-formally, just as a way of saying “this is a thing we recommend you do in a particular context”. |
Important
A key point to remember.
Note
Extra info to clarify a point or provide context.
Tip
A tip or trick worth knowing about.
Warning
Gotchas and things to be careful about.
Caution
More severe gotchas and things to watch out for.
This base template is designed to be an opinionated starting point for a modern Angular app, connecting to Firebase services for all backend functionality (auth, db, APIs, etc.)
At a high level:
- The Angular app (in the
app
subfolder) is primarily a single-page app (SPA) with some prerendered static pages, deployed to Firebase Hosting. This is where you build your UI, handle user interactions, and manage state, communicating with Firebase services using the Firebase JavaScript SDK. - For Firebase (in the
firebase
subfolder), you define and deploy your own functions (if needed), security rules, database indexes and hosting config. - You get certain functionality out of the box, such as a place for common code, authentication (with passwordless login), static website pages (which you fill in with your content), basic progressive web app (PWA) set-up, local Firebase emulators, test suites and so on.
Note
Make sure you go through the first part of the README for a list of the features you get out of the box. You'll also find instructions on setting up your own app from this base template.
For the rest of this document, we'll dive into the specifics of how things are set up and why.
🧠 Design decision |
---|
We use a simple folder-based monorepo structure with completely separate frontend (app ) and backend (firebase ) folders. Each folder is isolated from the other — i.e. they have their own package.json etc. files and are completely separate codebases (though see the common code and deploy sections below for how they do connect up). |
This isolation is important as Firebase Functions currently deploy ALL dependencies in the relevant package.json
and so it's important to keep the backend and frontend dependencies separate. It's also a useful separation of concerns when reasoning about how things work in your app.
Note
This does mean you have to manage, install and update dependencies separately, for each subfolder.
A VS Code workspace config is provided to work on both at the same time, in a single VS Code window. The workspace also provides settings and recommended extensions that will help your development experience.
Tip
Use the ./edit
script to open the workspace in VS Code (run this in the root of the repo).
🧠 Design decision |
---|
We've included a lightweight mechanism for sharing simple code and TypeScript types between the frontend and backend — the firebase/common folder. |
When using the same language (in this case TypeScript) for both the frontend and backend, it's useful to have a place for shared common code, usually types, interfaces, utility functions, etc. This is especially useful for shared data models (like Firebase document structures).
The firebase/common
folder is the place for this shared common code. We recommend exporting everything public in the firebase/common/index.ts
file (also known as a "barrel" file).
Warning
It's highly recommended to only put types, interfaces and very simple utility functions here, and to not rely on any external libraries. Where you do want to rely on an external library (e.g. type-fest) make sure the library is added to both the app
and firebase
package.json
files.
Tip
In the Angular app code, import from @common
(which is an alias set up in the tsconfig
file).
In Firebase functions code, use relative imports to the firebase/common
folder.
🧠 Design decision |
---|
All local development is done against a locally running instance of the Firebase Local Emulator Suite, running with a demo project with no access to any live Firebase services. This does mean we can’t make easy use of Firebase services that aren’t supported by the local emulator (e.g. Remote Config). |
Note
To ensure that the Firebase emulators only run in “demo” mode, and never access any live Firebase services, we start the emulators with --project=demo-local
. From the docs, a project ID starting with the prefix demo-
will force the emulators to run in demo mode.
Note that this also ensures that the functions running within the emulators (from the firebase/functions
folder) can only access locally emulated services.
When you run the pnpm dev
command in the firebase
folder, the Firebase Local Emulator Suite is started up and connected to all your functions code (which are watched and compiled whenever changed) and security rules. Then, when you run the dev server for the Angular app (pnpm dev
in the app
folder) it's configured to use these emulators for all Firebase access, via the default environment config: app/src/environments/environment.ts
.
All data in the emulators is persisted to the firebase/local
folder (on shutdown), so you can stop and start the emulators and your data will still be there (on the same machine).
🧠 Design decision |
---|
Beyond local development, this base template assumes only one live Firebase project / environment (which you set up), as specified in the firebase/.firebaserc file.We believe this is a good simple set-up to get you started, and for the first phase of your project (going from 0 to 1), after which you can always add intermediate environments for testing and less risky deploys / rollouts |
This live project is your production environment — what your users will access, and where all your real data will live.
The deploy script will deploy to this live project (details below).
Tip
In the patterns example app (coming soon) we show you how to set up an intermediate staging environment, together with continuous deployment (CD), which requires a bit more set-up and configuration.
🧠 Design decision |
---|
Out of the box, all deployments happen from your local machine, controlled by you. |
The ./deploy
script in the root of the project is a simple script that performs the following steps:
- Asks for confirmation first before carrying on.
- Cleans the
firebase/dist
folder in case there is an older build. - Builds the Angular app (in production mode), which outputs to the
app/dist
folder. - Copies over just the browser output from the Angular build to the
firebase/dist/app
folder (i.e. ignores the server folder).- This is because we don't use server-side rendering (SSR) (details below).
- Runs the
pnpm deploy:live
command in thefirebase
folder to trigger the Firebase deploy process to thelive
project.- This includes all the Firebase Functions code, security rules, the app assets and Firebase Hosting config.
Note
This is designed to be run locally only (not as part of a CI/CD pipeline) as it benefits from your locally authenticated firebase
CLI.
For CI/CD you'd want to update the GitHub Actions pipeline to deploy to Firebase, which would involve setting up a service account and securely storing the credentials in GitHub Secrets so that the pipeline can authenticate with Firebase. We show you how to do this in the patterns example app (coming soon).
Important
We highly recommend waiting for the GitHub Actions pipeline to complete successfully before deploying from your local machine (which requires you to push your changes to the remote repository on GitHub and create a pull request (PR), or push directly to the main
branch).
🧠 Design decision |
---|
We use all the modern Angular features, such as inject function, signals, signal inputs, signal outputs, router input bindings, control flow, etc. |
🧠 Design decision |
---|
We don't use Angular modules (i.e. @Module ) for our own code — we've chosen to go all-in on Angular's recent standalone approach. So we only ever define (and prefer to import) standalone components, directives, etc.The base template has configured the Angular CLI generator to always set the standalone: true flag on any components, directives, etc. you generate. |
🧠 Design decision |
---|
We make two key decisions about Angular components (which all components generated from the Angular CLI use):
|
Note
With the OnPush
change detection, there is a caveat that forms sometimes don't behave well, so in rare cases you'd need to use the ChangeDetectorRef
to manually mark a component for change detection.
🧠 Design decision |
---|
We also configure the Angular CLI generator to only ever generate flat components, directives, etc. This means they don't get their own folder (for the file itself and the test file), as we think this is unnecessary given modern IDEs and the ability to easily browse and load files. |
Here are the main folders and files for the frontend app/src
folder (some left out for brevity):
app/src
└─ app
└─ {your feature folder(s)}
└─ data
└─ feature
└─ ui
└─ util
└─ login
└─ {login feature files}
└─ shared
└─ auth
└─ {auth store, services, guards, etc.}
└─ firebase
└─ {firebase helpers}
└─ logger.ts
└─ runtime.service.ts
└─ website
└─ feature
└─ {components for the static pages}
└─ ui
└─ {UI components for the website feature}
└─ website-shell.component.ts
└─ website.routes.ts
└─ app.component.ts
└─ app.routes.ts
└─ assets
└─ {images, icons (incl PWA icons), fonts, etc.}
└─ environments
└─ {environment files - live, test and local}
└─ test
└─ helpers
└─ {test helpers}
└─ index.html
└─ manifest.webmanifest
└─ styles.scss
We'll refer to these in the rest of the document.
Note
There are some files at the root of the /app
folder (not shown in the listing above) which we'll touch on in later sections. These include: angular.json
, prerendered-routes.txt
, ngsw-config.json
and tailwind.config.js
.
🧠 Design decision |
---|
For our Angular app code (within app/src/app ) we prefer a flat folder structure, with top-level "feature" folders and a single shared folder. |
🧠 Design decision |
---|
We split sections of our app — e.g. the website — into feature folders, placed within the app/src/app folder, with shared features and utilities (accessible by any other feature) placed within the app/src/app/shared folder.You are welcome to deviate from this though, where it makes sense for you. |
✅ Pattern |
---|
We highly recommend separating the code within the top-level feature folders into the following subfolders: data , feature , ui and util . And trying to keep these at one hierarchical level. We've found that this is a great starting folder structure (and general architecture) which helps you quickly find stuff, whilst spending minimal time on figuring out what goes where. |
The data
folder is for (most) state management and data access services. Page and smart components go in the feature
folder, whilst presentational components go in the ui
folder. And the util
folder is for standalone utilities.
This is a recommended folder structure based on Nx's suggested library types.
For features within the shared
folder you should follow the same structure, except you probably won't need a feature
subfolder within each shared feature since these are shared bits of code for use elsewhere.
As things grow you may need to adapt and tweak this structure (e.g. to add another level in the hierarchy) — we show you how to tackle this in the patterns example app (coming soon).
🧠 Design decision |
---|
Out of the box, we don't use server-side rendering (SSR). We do use prerendering for certain pages (configured explicitly), and everything else is fully dynamic (i.e. client-side only). |
Whilst Angular has very good support for server-side rendering (SSR) we don't make use of this in the deployed app as we want to be able to run the app wholly from static assets (i.e. no dynamic server required to render any pages).
Instead, we do make use of build-time prerendering for routes we explicitly specify in app/prerendered-routes.txt
file (currently the website home and about pages) — a static HTML file is built and served for each path specified there (with some additional Firebase Hosting and PWA configuration required to support these).
And then everything else in the app is fully dynamic (i.e. rendered on the client) — the special index.csr.html
generated by the Angular build is used in the Firebase Hosting config and the PWA set-up as the file to serve for all non-prerendered routes (more details below).
Note
In this tech stack, we haven't included a CMS or dynamic page generation system (e.g. from Markdown files). Instead, we use static prerendered pages — via regular Angular components and routes — for the website content (and any other static pages).
This is a simple and effective way to add static content, but will probably not scale up to larger uses cases (like a blog with hundreds of posts, or a full-on marketing website). At that point, you'd want to consider hosting your website as a separate static site (which has added benefits like: you can deploy it faster and more frequently).
For the build-time prerendering of pages, we:
- Configure the
prerender
option inangular.json
to prerender all paths defined in theapp/prerendered-routes.txt
file.- We also set
"discoverRoutes": false
so only the routes we explicitly specify are prerendered.
- We also set
- Specify all static paths we want prerendered, in the aforementioned
prerendered-routes.txt
file.- Out of the box, we have the website home page (
/
) and the about page (/about
).
- Out of the box, we have the website home page (
So, when we run the production build (pnpm build
) Angular will output separate static HTML files for the prerendered routes.
Note
If you want to add another static website page (or other prerendered route) follow the relevant "how-to" guide in the README
.
Given we have a mix of prerendered static and fully dynamic pages, we have to configure Firebase Hosting to serve the right HTML file for the right path, and also configure the PWA set up for proper caching and default index template serving.
Important
In a typical single-page app (SPA) without any server side rendering or static page generation, you'd serve a static index.html
file for all paths requested. This file would usually contain very little UI, and then bootstrap the app and handle routing, data fetching, templating, etc. on the client-side (all handled by your framework).
However, in our case, the index.html
file is now the static (prerendered) website home page (which still bootstraps the Angular app when it loads client-side), which we wouldn't want to serve for all routes in our app as it contains content for the home page. And we have a mix of static pages and fully dynamic pages that need to work regardless of whether they are requested directly (from Firebase Hosting) or within the single-page app (client-side). As part of the build, Angular outputs a special index.csr.html
file which we make use of for all routes not covered by the prerendered pages.
For the static pages (prerendered), we:
- Explicitly define these paths in the
app/prerendered-routes.txt
file — these tell Angular to build static HTML files for these paths only. - Add an entry in the
firebase/firebase.json
file (under thehosting.rewrites
key) to serve the relevant prerendered HTML file for each path.- E.g.
index.html
for the website home page.
- E.g.
- Also want to make sure these paths are never prefetched or cached by the PWA service worker (because we always want to get the fresh content for these pages), so we add an exclusion for each in the
app/ngsw-config.json
file (under thenavigationUrls
key).- For the two static pages provided in the base template, the entries are:
"!/$", "!/about"
.
- For the two static pages provided in the base template, the entries are:
Then, for the rest of the fully dynamic pages, we:
- Add an entry in the
firebase/firebase.json
file (under thehosting.rewrites
key) to serve the special/index.csr.html
file for all paths that aren't explicitly covered by the static (aka prerendered) pages.- This is known as a "catch-all" rule, and MUST be the last item in the list of rewrite rules.
- Configure the PWA service worker (in the
app/ngsw-config.json
file) to use the same"/index.csr.html"
path as the default "index" file to serve for all paths not covered by those defined in thenavigationUrls
key.- I.e. this will be used by the service worker for all dynamic pages.
- In this same file, we also add
"/index.csr.html"
to the list of prefetched URLs so it can be cached by the service worker.
Note also: in the firebase/firebase.json
file (under the hosting
key) we set "cleanUrls": true
and "trailingSlash": false
to normalize the behavior and ensure our static paths are served correctly.
Collectively, this allows us to seamlessly serve the right HTML file for the right path, whether it's a static page or a fully dynamic page, and ensures that the PWA service worker doesn't cache the static pages.
Whilst on the topic of Firebase Hosting, we also set up some caching headers in the firebase/firebase.json
file (under the hosting.headers
key) to ensure that certain assets are cached for a long time (e.g. images, fonts, etc.) and others are not cached at all (e.g. the service worker files and everything else).
🧠 Design decision |
---|
We use Angular's PWA capabilities, mainly the service worker support, and provide a basic PWA set-up out of the box, with a manifest, caching, icons and a simple in-app update notification. Once the app is loaded on a user's device (via the web browser, or from the home screen / app launcher) any new updates are downloaded behind the scenes and the user is informed when there's an update. |
The core of a PWA config is the manifest.webmanifest
file, which defines the app's name, icons, colors, etc. This is used by the browser to provide a more "app-like" experience when the user adds the app to their home screen / app launcher (depending on device capabilities). This file follows the regular PWA manifest spec. You'll need to customize this file to specify your app's name and branding.
For the Angular service worker, the app/ngsw-config.json
file is the main configuration, determining how to cache assets, handle updates, etc (docs).
The app/src/app/app.component.ts
file contains the logic for the in-app update notification, which checks for updates to the app and prompts the user to reload when a new version is available.
Note
Technically, a new "version" of an app is just a new build of assets, as defined by the assetGroups
in the ngsw-config.json
file. The service worker will automatically download (and cache) these new assets in the background and the trigger the in-app update notification.
🧠 Design decision |
---|
We use Angular Material (with Material 3) for UI components, and Tailwind CSS for styling. You can still create your own UI components or add in other libraries, if needed. You can also customize Tailwind CSS as you wish, by updating the app/tailwind.config.js config file. |
The app/src/styles.scss
file sets up both Angular Material (with a basic Material 3 theme with custom background and text colors) and Tailwind CSS styling. Here, we also provide styling overrides to make Angular Material work okay with the Tailwind CSS base styles.
Note
We scope all Tailwind CSS and custom styles within the #app
selector — we apply the app
ID on the <body>
element in the app/src/index.html
file and configure tailwind.config.js
to place all Tailwind-specific styles within the #app
selector, acting as a "namespace". When adding styles to the styles.scss
file make sure to add them within the #app
selector (already defined) to stick to this namespaced approach. This will hopefully allow for better overrides as you will have more specificity in your custom styles.
You can import and use Angular Material components within your components as usual (see the docs). And you can use Tailwind CSS classes in your HTML and SCSS files as you wish (see the docs).
🧠 Design decision |
---|
We use NgRx SignalStore for all state management (outside of components) in the Angular app. |
State management is a bit of a hot topic in the Angular community, and there are many ways (and libraries) to do it. One of the opinionated choices made as part of this curated tech stack is the use of NgRx SignalStore for all state management (outside of components). SignalStore is very well-designed and works hand-in-hand with Angular's Signals system.
Tip
The NgRx docs do a fantastic job of explaining why you would want to use a library like NgRx to manage state. The linked page covers the older (but still relevant) NgRx Store, but the principles are still applicable to SignalStore. Note that SignalStore is more lightweight and does not follow the Redux pattern, making it a bit simpler to use.
The base template uses SignalStore for the provided global auth store as well as the component-specific login flow store. We cover stores (and state management) in more detail in the example apps.
🧠 Design decision |
---|
We provide wrappers within the app/src/app/shared/firebase/ folders for accessing the various Firebase services from your Angular components, directives, services, etc. These are registered as global injectables so we can use Angular's dependency injection. |
The following special injection functions are provided for you to inject into your components, services, etc. to access the various Firebase services:
injectAuth()
— for the Firebase Authentication service.injectFirestore()
— for the Firestore service.injectRtdb()
— for the Realtime Database service.injectStorage()
— for the Firebase Storage service.injectFunctions()
— for the Firebase Functions service.
These return the underlying Firebase service instances provided by the Firebase JavaScript SDK.
Tip
The base template comes with rxfire
installed in the app's dependencies. This is a third-party library that provides a set of functions that bridge the gap between the Firebase JavaScript SDK and RxJS Observables. It's a great way to work with Firebase services in a more reactive way.
You can pass in the Firebase service instance returned from the aforementioned injection functions to the rxfire
functions.
🧠 Design decision |
---|
We use Firebase Authentication for all authentication and login functionality, and provide a passwordless login flow out of the box. |
A critical component of any app that provides user-specific capabilities is authentication. The base template comes with an auth store, auth guard, login page, passwordless login flow and logout flow out of the box.
To use the auth store: inject the global AuthStore
service into your component, service, directive, etc. using inject(AuthStore)
. You then have access to the state of the auth store. Note that this auth store automatically connects to Firebase Authentication and listens for changes, when your app starts up.
To use the auth guard:
- For a route that requires a logged-in user: add
canMatch: [authGuard('authed')]
to your route definition.- When a user is not logged in, they will be redirected to the login page, and taken back to the previous page after logging in.
- For a route that requires that no user is logged in: add
canMatch: [authGuard('not-authed')]
to your route definition.- When a user is logged in, they will be redirected to the home page.
- Note that you probably won't need this — it's currently used for the
/login
route as we don't want already logged in users to access the login page.
For fully public pages, don't use the auth guard.
To try out the login flow run the app locally and click on the "Login" button.
Tip
For local development, when you perform the login flow, you can get the special login link from the Firebase emulator log output (from the terminal output where you ran pnpm dev
for the firebase folder) — no emails are actually sent from local development!
Note
Firebase Authentication does not provide server-side sessions, which is not a problem for us as we don't use server-side rendering (SSR), and for any server-side functionality we use Firebase Functions (which has access to the auth token in each request). All authentication is carried out and managed client-side using the Firebase JavaScript SDK.
This does mean that in the auth guard we need a check to see if we're running server-side and then short-circuit the logic and return false
. Note that, currently, this is only applicable to local development, since we don't use SSR in production. Once the page loads in the browser then the usual client-side auth check takes over when the Angular app hydrates (i.e. fully loads up). You may see the error ERROR RuntimeError: NG04002: Cannot match any routes.
in the dev process output — you can safely ignore this as it will only happen in local development.
🧠 Design decision |
---|
We provide a simple logger utility in app/src/app/shared/logger.ts , which uses consola under the hood. |
During development, and even in production, it's useful to log errors, messages and data to the console to inspect the running state of the app and debug potential issues.
Use the logger utility to create a logger instance for every file in which you need to perform logging. E.g. const logger = createLogger('MyComponent');
— this prepends the logging output with "MyComponent"
.
Important
The logging level is configured in the relevant environment config file. For example, for local development the log level is set to 5
in the app/src/environments/environment.ts
file, whilst it's set to 0
(i.e. only fatal and errors) in the live environment.
See https://unjs.io/packages/consola#log-level for the different log levels.
We suppress ALL logging in tests by default, to prevent noisy test output, but you can enable it by changing the log level in the test environment config file (app/src/environments/environment.test.ts
).
🧠 Design decision |
---|
We use ng-mocks across most of the app test suites, to simplify boilerplate and mock out dependencies. |
The Angular test suites use the default Jasmine and Karma set-up that comes with an app generated by the Angular CLI.
We encourage use of ng-mocks to simplify a lot of the boilerplate that comes with writing tests for Angular components, services, directives, etc, and to mock out dependencies in an easier way. Though feel free to avoid it in cases where it doesn't add value (but try to keep things consistent in usage).
Most of the components, services, etc. provided in the base template have corresponding test suites.
Tip
We cover testing in more detail in the example apps.
🧠 Design decision |
---|
We use ESLint for linting and Prettier for code formatting. Running pnpm lint performs the linting only, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |
The config for linting is in app/eslint.config.js
and for formatting in app/.prettierrc
.
We also integrate prettier-plugin-tailwindcss
to format Tailwind CSS classes in your HTML and JavaScript files.
Here are the main folders and files for the backend (some left out for brevity):
firebase
└─ common
└─ {shared types, interfaces, utility functions, etc.}
└─ dist
└─ {compiled app code goes here - gitignored}
└─ functions
└─ src
└─ {your functions folders and code}
└─ index.ts
└─ package.json
└─ tsconfig.json
└─ local
└─ {local emulator data persistence - gitignored}
└─ test
└─ {mainly security rules tests}
└─ database.rules.json
└─ firebase.json
└─ firestore.indexes.json
└─ firestore.rules
└─ storage.rules
We'll refer to these in the rest of the document.
🧠 Design decision |
---|
Whilst, typically, projects that use Firebase mix the output of firebase init in the same folder as the frontend app, we've chosen to separate ALL Firebase bits out into a dedicated folder — firebase — with no shared dependencies. We also add a place to put common code firebase/common that the app can import from. |
🧠 Design decision |
---|
We use Firebase Functions for all backend code, and write these in TypeScript. Currently, we don't provide an opinionated set-up for how to write and organize your functions code — you can do this however you want. |
All your Firebase Functions should be exported within the firebase/functions/src/index.ts
file (this is a Firebase Functions requirement).
We do recommend splitting out the actual functions into separate files within the firebase/functions/src
folder, and then importing and exporting them from within the index.ts
file.
Important
Make sure you go through the Firebase Functions docs to understand how to write functions and what you can do with them. Also make sure you understand the costs and operational model of Firebase Functions.
Tip
We make use of Firebase Functions extensively in the patterns example app (coming soon).
The base template comes with all security rules — for Firestore, Realtime Database and Storage — set to block all access by default. This is the most secure setting. As you build out your app you'll need to update these rules to allow the necessary access.
The relevant security rules files are:
firebase/firestore.rules
for Firestore.firebase/database.rules.json
Realtime Database.firebase/storage.rules
for Storage.
We also have some test suites set up to test out your security rules — see the firebase/test
folder.
Important
Make sure you go through the Firebase Security Rules docs to understand how to write rules and what you can do with them.
Note
We highly recommend having comprehensive test coverage of your security rules, as they are the gatekeepers to your backend and insecure rules can cause data leakage and other security issues.
🧠 Design decision |
---|
We use the Firebase Local Emulator Suite to test security rules locally and provide some test suites ready for testing out your security rules. You can also write unit and integration tests for any other code within the firebase folder, including Firebase Functions.We use Vitest as the testing framework. |
See the files within the firebase/test
folder for the security rules test suites.
Note
You can add any additional tests you want in this folder — Vitest will pick these up as long as they contain ".test." or ".spec." in their filename.
Tip
We cover more Firebase testing, such as functions testing and integration testing, in the patterns example app (coming soon).
🧠 Design decision |
---|
We use ESLint for linting and Prettier for code formatting. Running pnpm lint performs the linting only, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |
The config for linting is in firebase/eslint.config.js
and for formatting in firebase/.prettierrc
.
🧠 Design decision |
---|
We provide a basic GitHub Actions pipeline (.github/workflows/ci.yml ) that runs linting, tests and builds for both the app and firebase parts.This pipeline is configured to run on every pull request commit and on commits pushed to the main branch. |
Use the Firebase Console to manage and monitor your Firebase services.
Important
Since Firebase is a pay-as-you-go service, it's important to keep an eye on your usage and costs. You can set up budget alerts in the Firebase console to notify you when you're approaching certain thresholds.
Caution
Firebase currently doesn't have a way to set a hard limit on costs, so you need to be vigilant about monitoring your usage and costs, and how you build features to make sure of Firebase services. Ultimately, you are responsible for the costs incurred by your use of services like Firebase.
We cover some strategies for managing costs in the patterns example app (coming soon).