This is exactly the situation I address in this blog post.
The basic strategy is to use async imports (for TypeScript) or a JavaScript require inside the function itself in order to defer the loading of the per-function script file until execution. This prevents one function's dependencies from being loaded for all functions.
Here's what a couple functions would looks like in TypeScript:
import * as functions from 'firebase-functions'
export const httpFn =
functions.https.onRequest(async (request, response) => {
// Move the implementation of this function to fn/httFn.ts
await (await import('./fn/httpFn')).default(request, response)
})
export const firestoreFn =
functions.firestore.document("users/{id}")
.onCreate(async (snapshot, context) => {
// Move the implementation of this function to fn/firestoreFn.ts
await (await import('./fn/firestoreFn')).default(snapshot, context)
})
Then, for the HTTP function, the implementation in fn/httpFn.ts looks like this:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()
// Note: Need to be explicit about parameter types here for HTTP
// type functions, to support type safety and IDE assistance.
export default async (
request: functions.https.Request,
response: functions.Response
) => {
// Here we can use the admin SDK to get a document
const snapshot = await admin.firestore()
.collection('users')
.doc('uid')
.get()
const data = snapshot.data()
response.send(data)
}
In JavaScript, the strategy is the same. You would just use a require instead of await import.