This document describes the project structure required for Firebase DevOps Toolkit to work correctly.
The toolkit expects a microservices architecture where:
- Each service is a separate directory
- A root
index.jsexports all functions from all services - Firebase Functions are exported using the standard pattern
your-project/
├── services/ # Main services directory
│ ├── index.js # ROOT EXPORTS (critical!)
│ ├── package.json # Dependencies
│ ├── firebase.json # Firebase configuration
│ ├── essential-services.txt # Services to deploy (optional)
│ │
│ ├── shared/ # Shared utilities (optional)
│ │ └── libs/
│ │ ├── firebaseAdmin.js
│ │ └── ...
│ │
│ ├── public-api-service/ # Example service
│ │ └── index.js # Service exports
│ │
│ ├── auth-service/ # Another service
│ │ └── index.js
│ │
│ └── {service-name}/ # Pattern: service directories
│ └── index.js
│
├── scripts/ # Project scripts (optional)
│ └── orchestrate.sh # Your project wrapper
│
└── docs/ # Documentation (optional)
This file must export all functions from all services. Firebase Functions discovers what to deploy by reading this file.
// services/index.js
/**
* Root Firebase Functions Entry Point
* Exports all service functions for deployment
*/
// Import and re-export from each service
const publicApiService = require('./public-api-service/index.js');
exports.getProducts = publicApiService.getProducts;
exports.getProduct = publicApiService.getProduct;
exports.health = publicApiService.health;
const authService = require('./auth-service/index.js');
exports.login = authService.login;
exports.logout = authService.logout;
const orderService = require('./order-service/index.js');
exports.createOrder = orderService.createOrder;
exports.processOrder = orderService.processOrder; // Pub/Sub functionWhy this pattern?
- Firebase Functions scans
exports.*to discover functions - The toolkit parses this file to build deployment lists
- Each function name becomes the Cloud Function name
Each service directory must have an index.js that exports its functions:
// services/public-api-service/index.js
const { onRequest } = require('firebase-functions/v2/https');
// HTTP endpoint
exports.getProducts = onRequest({
region: 'europe-west1',
cors: true
}, async (req, res) => {
// Implementation
res.json({ products: [] });
});
// Another HTTP endpoint
exports.health = onRequest({
region: 'europe-west1'
}, (req, res) => {
res.json({ status: 'healthy' });
});For Pub/Sub functions:
// services/order-service/index.js
const { onMessagePublished } = require('firebase-functions/v2/pubsub');
exports.processOrder = onMessagePublished({
topic: 'order-topic',
region: 'europe-west1'
}, async (event) => {
const data = JSON.parse(Buffer.from(event.data.message.data, 'base64').toString());
// Process order
});Must include Firebase dependencies:
{
"name": "your-project-services",
"main": "index.js",
"engines": {
"node": "18"
},
"dependencies": {
"firebase-functions": "^4.0.0",
"firebase-admin": "^11.0.0"
}
}Configure Firebase Functions:
{
"functions": {
"source": ".",
"runtime": "nodejs18",
"codebase": "default"
}
}List services to deploy (one per line). Used by selective deployment:
# Core services
public-api-service
auth-service
# Processing services
order-service
notification-service
# Admin
admin-service
const { onRequest } = require('firebase-functions/v2/https');
exports.myEndpoint = onRequest({
region: 'europe-west1',
cors: true,
memory: '256MiB',
timeoutSeconds: 60
}, async (req, res) => {
res.json({ success: true });
});const { onMessagePublished } = require('firebase-functions/v2/pubsub');
exports.processMessage = onMessagePublished({
topic: 'my-topic',
region: 'europe-west1'
}, async (event) => {
const data = JSON.parse(Buffer.from(event.data.message.data, 'base64').toString());
console.log('Received:', data);
});const { onDocumentCreated } = require('firebase-functions/v2/firestore');
exports.onUserCreated = onDocumentCreated({
document: 'users/{userId}',
region: 'europe-west1'
}, async (event) => {
const userData = event.data.data();
console.log('New user:', userData);
});const { onSchedule } = require('firebase-functions/v2/scheduler');
exports.dailyCleanup = onSchedule({
schedule: '0 0 * * *',
region: 'europe-west1',
timeZone: 'Europe/Stockholm'
}, async (event) => {
console.log('Running daily cleanup');
});For code shared across services, create a shared/ directory:
services/
├── shared/
│ └── libs/
│ ├── firebaseAdmin.js # Firebase Admin SDK singleton
│ ├── logger.js # Logging utilities
│ └── validation.js # Input validation
│
└── my-service/
└── index.js # Uses: require('../shared/libs/logger')
Example shared module:
// services/shared/libs/firebaseAdmin.js
const admin = require('firebase-admin');
if (!admin.apps.length) {
admin.initializeApp();
}
exports.getFirestore = () => admin.firestore();
exports.getAuth = () => admin.auth();The toolkit uses function names from exports:
| Export Name | Cloud Function Name | URL Path |
|---|---|---|
exports.getProducts |
getProducts |
/getProducts |
exports.health |
health |
/health |
exports.processOrder |
processOrder |
(Pub/Sub, no URL) |
Best practices:
- Use camelCase for function names
- Use descriptive names:
getUserByIdnotget - Prefix by domain:
orderCreate,orderProcess,orderCancel
The toolkit automatically detects your project structure by looking for:
services/index.js- Root exportsservices/package.json- Node.js projectservices/firebase.json- Firebase configuration
If these exist, deployment commands will work:
./manage.sh deploy-local # Deploy to emulator
./manage.sh deploy-production # Deploy to Firebase
./manage.sh deploy-function --function getProducts # Single functionSee the template in templates/orchestrate.sh for a complete working example.
For a full reference implementation, check out the project structure documentation.
Cause: Root index.js doesn't export any functions.
Fix: Ensure all functions are exported:
// services/index.js
const myService = require('./my-service/index.js');
exports.myFunction = myService.myFunction; // Must export!Cause: Function exists in Firebase but not in your exports.
Fix: Either add the export or delete the function:
firebase functions:delete oldFunction --project PROJECT_IDCause: Service directory or index.js missing.
Fix: Ensure structure:
services/
├── my-service/
│ └── index.js # Must exist!
└── index.js # Must require('./my-service/index.js')
Documentation by SolidKey AB