invrt
invrt

Reputation: 709

Firebase Hosting + Cloud Function

Having diffuclty deploying my functions as well as hosting.

The thing is, I have gotten them to work independently on seperate branches, however..when trying to integrate both hosting and my cloud functions, it seems that my cloud function does not deploy. I'm not receiving any errors in my terminal, and when I click 'functions' in the firebase console, it's the default screen as if no functions have deployed.

This is my firebase.json for hosting + functions deployment. Hosting works here, but functions are not deploying

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      },
      {
        "source": "/api/v1/**",
        "function": "webApi"
      }
    ]
  },
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ],
    "source": "functions"
  }
}

Heres my firebase.json with functions only and no hosting

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/api/v1/**",
        "function": "webApi"
      }
    ]
  },
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ],
    "source": "functions"
  }
}

And heres my functions/index.js

const functions = require('firebase-functions')
const admin = require('firebase-admin')
const serviceAccount = require('./serviceAccount.json')

const express = require('express')
const bodyParser = require('body-parser')

const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
const json2csv = require('json2csv').parse

admin.initializeApp({
  ...,
})
const db = admin.firestore()

const app = express()
const main = express()

main.use('/api/v1', app)
main.use(bodyParser.json())
exports.webApi = functions.https.onRequest(main)

app.get('/test', (request, response) => {
  response.send('API TEST')
})

app.get('/surveys', (request, response) => {
  const surveyCollection = db.collection('/surveys')
  return (
    surveyCollection
      .get()
      // eslint-disable-next-line promise/always-return
      .then(querySnapshot => {
        let surveyList = []
        querySnapshot.forEach(doc => {
          const survey = doc.data()
          surveyList.push(survey)
        })
        response.send(surveyList)
      })
  )
})

app.get('/surveys/:survey', (request, response) => {
  const surveyId = request.params.survey
  const userAnswers = db.collection(`/surveys/${surveyId}/submissions`)
  return (
    userAnswers
      .get()
      // eslint-disable-next-line promise/always-return
      .then(querySnapshot => {
        let surveySubmissions = []
        querySnapshot.forEach(doc => {
          const userSubmission = doc.data()
          surveySubmissions.push({
            ..._.mapValues(userSubmission.answers, getObjectValues), // format answers
            ...userSubmission.anonUser,
          })
        })
        response.setHeader('Content-disposition', 'attachment; filename=cna.json')
        response.set('Content-Type', 'application/json')
        response.status(200).send(surveySubmissions)
      })
      .catch(error => {
        console.log(error)
      })
  )
})

Hosting + functions branch, I type 'firebase deploy'

terminal output:

    i  deploying functions, hosting
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint \surveyplus-cna\functions
> eslint .

+  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
+  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (82.32 KB) for uploading
+  functions: functions folder uploaded successfully
i  hosting[surveyplus-effd5]: beginning deploy...
i  hosting[surveyplus-effd5]: found 30 files in build
+  hosting[surveyplus-effd5]: file upload complete
i  functions: updating Node.js 8 function webApi(us-central1)...
+  functions[webApi(us-central1)]: Successful update operation.
i  hosting[surveyplus-effd5]: finalizing version...
+  hosting[surveyplus-effd5]: version finalized
i  hosting[surveyplus-effd5]: releasing new version...
+  hosting[surveyplus-effd5]: release complete

+  Deploy complete!

On cloud function only branch firebase deploy output

=== Deploying to '...'...

i  deploying functions, hosting
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint \surveyplus-cna\functions
> eslint .

+  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
+  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (82.33 KB) for uploading
+  functions: functions folder uploaded successfully
i  hosting[surveyplus-effd5]: beginning deploy...
i  hosting[surveyplus-effd5]: found 30 files in build
+  hosting[surveyplus-effd5]: file upload complete
i  functions: updating Node.js 8 function webApi(us-central1)...
+  functions[webApi(us-central1)]: Successful update operation.
i  hosting[surveyplus-effd5]: finalizing version...
+  hosting[surveyplus-effd5]: version finalized
i  hosting[surveyplus-effd5]: releasing new version...
+  hosting[surveyplus-effd5]: release complete

+  Deploy complete!

Upvotes: 2

Views: 3748

Answers (1)

samthecodingman
samthecodingman

Reputation: 26171

Your Cloud Functions have deployed correctly, as I can call the /api/v1/test route directly just fine (at the time of writing).

However, in your firebase.json file, the order of your rewrites is incorrect as highlighted in the docs:

Important: Within the rewrites attribute, the Hosting response will obey the rule specified by the first source glob that captures the requested path.

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/api/v1/**",
        "function": "webApi"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ],
    "source": "functions"
  }
}

So instead of sending /api/v1/test calls to be handled by your Cloud Function, they were incorrectly sent to your React app instead. Swapping the order should fix this.

Another note, if you intend all your calls to be formatted and parsed as JSON, you should reorder the main.use calls. I'd also use an express Router instead of a complete express() instance.

const main = express(); // full express instance
const app = express.Router(); // "child" express instance

main.use(bodyParser.json()) // parse body as json, then hand over to app
main.use('/api/v1', app)

Upvotes: 5

Related Questions