Search code examples
typescriptfirebaseexpressgoogle-cloud-functions

How do use `defineSecret` with express and `onRequest` in firebase function V"?


I am upgrading my firebase functions to V2 and I want to use the new way of managing secrets via defineSecret described here: https://firebase.google.com/docs/functions/config-env?hl=en&gen=2nd

It is not clear to me how this works with onRequest to host a function via express that can be accessed from outside the firebase framework.

Here is a minimal version of my setups (which worked with firebase functions V1):

import express from "express"
import cors from "cors"
import { onRequest } from "firebase-functions/v2/https"

const app = express()

app.use(
  cors({
    origin: function (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) {
      if (!origin) return callback(null, true)
      return callback(null, true)
    },
  }),
)

import { handleNewProfile } from "./handlers/profiles" // needs a secret key for external service like sendgrid


app.post("/profiles/", handleNewProfile)
export const api = onRequest({ region: "europe-west1" }, app) // how to pass secret to app?

I can add the secrets to the first argument like this:

import { defineSecret } from "firebase-functions/params"
{ region: DEFAULT_REGION, secrets: [secretDiscordToken] }

But how do I hand it down to handleNewProfile?

I have not found this case in the docs and ChatGPT also does not know! :)

Any help would be greatly appreciated.


Solution

  • Thanks for the help Doug Stevenson!

    I am including a complete answer since it may be helpful to the next person!

    I first thought I needed to hand down a secret explicitly in the file where it is defined via defineSecret and passed down to a firebase function like onRequest.

    What I learned is, that any function called via onRequest can access the secret. There is no need to hand it down explicitly.

    Below is a working minimal example of how it can work.

     // ./handlers/secretTest.ts
        import { Request, Response } from "express"
        import { defineSecret, defineString } from "firebase-functions/params"
        const blubb = defineString("BLUBB")
        const secretTest = defineSecret("TEST_NOT_REALLY_SECRET")
        
        const printSecret = (req: Request, res: Response) => {
          const envVar = `${blubb.value()}`
          const secret = `${secretTest.value()}`
          res.send(`secret: ${secret} - envVar: ${envVar}!`)
        }
        
        export { printSecret }
    

    Here is the main file where the secret is added to the dependency array of onRequest. Even though there are a few indirections (with using express) until the secret is referenced, it all works:

    // index.ts
    import express, { Request, Response } from "express"
    import cors from "cors"
    import { onRequest } from "firebase-functions/v2/https"
    import { defineSecret } from "firebase-functions/params"
    import { printSecret } from "./handlers/secretTest"
    const secretTest = defineSecret("TEST_NOT_REALLY_SECRET")
    
    const app = express()
    
    app.use(
      cors({
        origin: function (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) {
          // allow requests with no origin
          // (like mobile apps or curl requests)
          if (!origin) return callback(null, true)
          // return the callers origin - Access-Control-Allow-Origin: <origin>
          // Note for testing locally: Some browsers may reject "localhost" as origin.
          return callback(null, true)
        },
      }),
    )
    
    app.get("/test", printSecret)
    
    export const api = onRequest({ region: "europe-west1", secrets: [secretTest] }, app)