Skip to content

Latest commit

 

History

History
219 lines (140 loc) · 11.3 KB

nx-firebase-functions.md

File metadata and controls

219 lines (140 loc) · 11.3 KB

Firebase Functions

Nx-Firebase Functions

Nx-Firebase functions are generated as individual Nx application projects, separately to the Nx-Firebase application.

  • This allows for multiple functions to be generated in a single workspace, each with their own src/main.ts entry point and package.json file.

  • The default src/main.ts template is the same one generated by the Firebase CLI and defaults to using 2nd gen cloud functions. This file is of course just a starting point and can be modified as needed.

  • Each function project is a buildable Typescript node-based application, which is compiled and bundled using esbuild.

  • Each function project can export one or more firebase cloud functions.

  • Each function project must be linked to a single Firebase application project in the workspace, which ensures all functions can be managed by the nx-firebase plugin.

  • Functions can import code from Nx shared libraries and esbuild will tree-shake and bundle the code into a single output file.

  • You rename or move Firebase function projects in your Nx workspace, and use the plugin sync command to keep your firebase.json configuration file in sync.

  • Functions can be tested, linted, built and deployed using the Nx CLI

package dependencies

  • Functions support deployment with npm, pnpm or yarn package managers.

Generating a new function

Generate a new Firebase function using:

  • nx g @simondotm/nx-firebase:function

OR

  • nx g @simondotm/nx-firebase:func
Options Type Description
name required the project name for your function
--app=<app-project-name> required the firebase app this function will be a dependency of
--directory=dir optional the directory this function will be located in
--format=<'cjs' or 'esm'> default 'esm' specify if esbuild should generated commonJs or ES6 output
--runTime=<node versions> optional the nodejs runtime you wish to use for this function - 14, 16, 18, 20
--tags optional tags to set on the new project
--setParserOptionsProject optional set the parserOptions.project in the tsconfig.json file
--projectNameAndRootFormat optional derived or as-provided (see Nx docs for projectNameAndRootFormat)

Building a Firebase function

To build your firebase function use this command:

  • nx build your-firebase-function-project-name

This will use esbuild to compile & bundle the input function Typescript source code to:

  • dist/apps/your-firebase-function-project-name/main.js - The bundled function code, in a single ESM format output file
  • dist/apps/your-firebase-function-project-name/package.json - The ESM format package file for firebase CLI to process and deploy

Firebase Function Dependencies & Package Managers

When building a function, Nx will automatically generate a package.json file in the output dist directory, which contains all package dependencies used by the function.

Nx will also generate a pruned package-lock.json, yarn.lock or pnpm-lock.yaml file in the function project root, depending on the package manager used in the workspace, which ensures your deployed function has exactly the same dependencies in the cloud as it does locally.

Deploying a Firebase function

To deploy all of your firebase function projects use:

  • nx deploy your-firebase-app-name --only:functions

To deploy one of your firebase function projects use this command:

  • nx deploy your-firebase-function-name

To deploy a single function from within a firebase function project that exports multiple functions, use this command:

  • nx deploy <codebase> --only functions:<codebase>:function-name

Where <codebase> is whatever name you gave your nx-firebase:function project.

IMPORTANT NOTE: Newly generated function projects do not deploy out-of-the-box. This is because the default Firebase CLI template for main.ts does not include code to call initalizeApp() so you will need to add this yourself:

   import { initializeApp } from "firebase-admin/app";
   initializeApp()

Testing & Linting your firebase function

  • nx test your-firebase-function-name
  • nx lint your-firebase-function-name

Managing Functions in your Workspace

You can use the parent Firebase app to build, test, lint, watch and deploy all of your functions at once.

  • nx build your-firebase-app-name
  • nx test your-firebase-app-name
  • nx lint your-firebase-app-name
  • nx watch your-firebase-app-name
  • nx deploy your-firebase-app-name

Note that there is no serve target for individual function projects, since serving only makes sense at a firebase app level with the Firebase Emulator suite, so use the following command instead:

  • nx serve your-firebase-app-name

See Firebase Application Targets for more details.

Functions & Nx-Firebase Applications

How Nx-Firebase function apps are linked to Nx-Firebase apps

The Nx-firebase plugin requires that Firebase function projects must always be a dependency of a single Firebase application project:

  • This approach allows for multiple firebase projects in a single Nx workspace
  • It ensures all functions can be managed by the nx-firebase plugin
  • Function application projects are added as implicitDependencies to the parent Firebase application, which ensures we can test, lint, build & deploy all functions from the top level Firebase application
  • All functions share the same Firebase --config CLI option as the parent Firebase Application
  • All functions share the same Firebase --project CLI option as the parent Firebase Application
  • You can create as many Firebase function projects as you like
  • Firebase function apps can export either just one or multiple firebase cloud functions
  • When running the Firebase emulator using serve, all firebase function applications are built using watch mode, so local development is much more convenient

Functions & Firebase Config Codebases

When new Firebase function applications are generated in the workspace:

  • They are automatically added to the functions[] declaration in the project's firebase.json config file using the firebase CLI's codebase feature
  • The codebase name assigned to the function in the config is the function applications project name.
  • When using firebase deploy, the CLI will deploy all codebase's declared in the firebase config file

Functions & ESBuild

esbuild is configured in the function's project.json to only bundle 'internal' source local to the workspace:

  • Import paths using TS aliases to @nx/js libraries will be resolved as internal imports.
  • All external imports from node_modules will be added to the package.json as dependencies, since there is no good reason to bundle node_modules in node applications.

Using ES Modules output

esbuild is also configured by default to always output bundled code as esm format modules:

  • This ensures tree-shaking is activated in the bundling process
  • Firebase functions with Node 16 or higher runtime all support ES modules
  • The bundled output code in dist is much cleaner to review
  • We are only specifying that the output bundle is esm format. The input source code sent to esbuild is Typescript code, which effectively uses ES6 module syntax anyway
  • Therefore, it is not necessary to change your workspace to use esm format modules to use this plugin since esbuild builds from Typescript source code, not compiled JS.

Using CommonJS output

If you still use Node require() in your Typescript function code, the default esm output setting for esbuild may not work. Your options are:

  1. Refactor your code to use import instead of require
  2. Modify the function project.json to set esbuild format to ['cjs']
  3. Generate your function applications with the --format=cjs option

Note that using cjs output may prevent tree-shaking optimizations.

Why ESBuild?

While Webpack and Rollup are viable options for bundling node applications:

  • esbuild is designed for node,
  • it is very fast
  • it optimizes the output using tree-shaking, which is great for fast cold starts
  • and it works very simply out of the box with Nx without any need for additional configuration files.

If you want to try Webpack or Rollup, just change your build target in the function's project.json accordingly.

The Nx Webpack bundler may be required for projects that require Typescript decorators such as NextJS.

Why not minify?

This plugin does not set or recommend the minify option for esbuild.

  • It is not really necessary for cloud function node runtime environments
  • Obfuscation of the code is not necessary since it executes in a private server-side environment
  • There is minimal (if any) performance benefit to be had
  • If exceptions occur in the cloud run, stack traces will be readable if code is not minified

Node Runtimes for Firebase Functions

Firebase Functions are deployed by the Firebase CLI to specific Nodejs runtime environments.

The required runtime is automatically set by the nx-firebase plugin function generator, but can be manually changedt in the firebase.json configuration as a definition (eg. nodejs16, nodejs18 etc.):

  "functions": [
    {
      "codebase": "firebase-project1",
      "runtime": "nodejs16",
      ...

    }
  ],

Runtimes are recommended to be set to the same value for all functions in a project.