Firebase projects with NX monorepo
At Oddbit, we work with Nx monorepo for our Firebase projects. Using Nx has been straightforward; there’s no need for special consideration beyond adjusting your firebase.json
to accommodate Nx’s recommended structure for best practices.
In our case, with Tanam CMS, we structure our Firebase projects by placing cloud functions and the app-hosting application under the apps folder, while shared libraries, such as models and utilities, live under libs.
Here’s a sample of our project structure:
project-root/
├─ apps/
│ ├─ app-hosting/
│ ├─ cloud-functions/
├─ libs/
│ ├─ models-backend/
│ ├─ models-frontend/
│ ├─ models-shared/
├─ .firebaserc
├─ apphosting.yaml
├─ firebase.json
├─ firestore.indexes.json
├─ firestore.rules
├─ nx.json
├─ storage.rules
Benefits of Using Nx in Firebase Projects
There are many advantages to using Nx. Here’s how we use it at Oddbit and why it’s our go-to toolset for Firebase projects.
Sharing Code Between App and Functions
One of the most common patterns we encounter is sharing models between cloud functions and the client application. These models represent our domain data, and Nx helps streamline cross-boundary code sharing. This means we don’t need complicated bundling strategies to ensure the shared code gets built and copied to the right places.
NPM Dependency Boundaries
When sharing models and utilities between functions and client apps, we sometimes run into issues where certain NPM libraries work only on the client or backend.
NX helps us to enforce module dependencies by tagging and declaring relationship constraints. By configuring our project with backend, frontend, and shared, we can create a set of rules that only allow for frontend apps to import frontend libraries and similarly for backend.
How this practically applies in the project for example, Firebase itself has a client SDK and an admin SDK
for cloud functions. Occasionally, we need the same conceptual features in our models or utilities, but with slight variations depending on the environment (client or backend).
For instance, our models use FieldValue and Timestamp, which must be referenced correctly depending on whether the client SDK or admin SDK is in use.
Managing Cross Dependencies
As with sharing models and utilities, code sharing also extends to shared dependencies, such as Axios, Zod, encryption libraries, etc. Nx makes managing these dependencies easy by centralizing them in a single package.json
. This prevents version mismatches between different parts of the codebase, ensuring everything stays consistent.
Centralized Configurations
Nx also helps us maintain centralized configurations for tools like ESLint and TypeScript (tsconfig
). This ensures all apps and libraries across our monorepo follow the same rules and conventions, making the codebase more maintainable and predictable.