simondotm / nx-firebase

Firebase plugin for Nx Monorepos
https://www.npmjs.com/package/@simondotm/nx-firebase
MIT License
175 stars 31 forks source link

Can't deploy with 2.0.0-beta.1 #128

Closed pikilon closed 11 months ago

pikilon commented 11 months ago

Hi, I am in the process to update from 1.1.0-beta.0 to 2.0.0-beta.1 and I followed the documentation (good documentation, thanks) by creating 2 projects, one for firebase and one for the cloud-functions (new from scratch with the default src files). the folder structure:

I can build firebase and I checked it creates a package.json (but without the node_modules with the dependencies) and a main.js with the default file:

// apps/cloud-functions/src/main.ts
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
admin.firestore().settings({
  ignoreUndefinedProperties: true
});
var helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", { structuredData: true });
  response.send("Hello from Firebase!");
});
export {
  helloWorld
};

when I run the deploy it exits with

functions: Loading and anaylzing source code for codebase cloud-functions to determine what to deploy
Serving at port 8738
shutdown requested via /__/quitquitquit
Error: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error

And in the firebase-debug-log:

[debug] [2023-07-12T05:18:37.805Z] Building nodejs source
[info] i  functions: Loading and anaylzing source code for codebase cloud-functions to determine what to deploy 
[debug] [2023-07-12T05:18:37.807Z] Could not find functions.yaml. Must use http discovery
[debug] [2023-07-12T05:18:37.811Z] Found firebase-functions binary at 'my-project/node_modules/.bin/firebase-functions'
[info] Serving at port 8069

[debug] [2023-07-12T05:18:38.174Z] Got response code 400; body Failed to generate manifest from function source: TypeError: admin.initializeApp is not a function
[info] shutdown requested via /__/quitquitquit

I think the problem is something related to the dependencies on the dist folder and this is why it can't find the admin from the library:

 source: TypeError: admin.initializeApp is not a function

Some information that could be important to solve the issue: root firebase.json (the only one I have, not an empty one. I don't fully get where to put it)

{
  "database": {
    "rules": "apps/firebase/database.rules.json"
  },
  "firestore": {
    "rules": "apps/firebase/firestore.rules",
    "indexes": "apps/firebase/firestore.indexes.json"
  },
  "hosting": {
    "public": "apps/firebase/public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "storage": {
    "rules": "apps/firebase/storage.rules"
  },
  "functions": [
    {
      "codebase": "cloud-functions",
      "source": "dist/apps/cloud-functions",
      "runtime": "nodejs18"
    }
  ],
  "emulators": {
    "auth": {
      "port": 9099
    },
    "functions": {
      "port": 5001
    },
    "firestore": {
      "port": 8080
    },
    "database": {
      "port": 9000
    },
    "hosting": {
      "port": 5000
    },
    "pubsub": {
      "port": 8085
    },
    "storage": {
      "port": 9199
    },
    "eventarc": {
      "port": 9299
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

package.json dependencies:

   "@simondotm/nx-firebase": "^2.0.0-beta.1",

    "firebase-admin": "^11.9.0",
    "firebase-functions": "^4.4.1",
    "firebase-tools": "^12.4.3",

I updated to the latest before the installation of nx-firebase

Thank you for your great work and in advance for your help

simondotm commented 11 months ago

Hi @pikilon ,

Thankyou for reporting this, and for the detail - this is super useful.

I agree that the problem will be the empty dependencies in your dist package.json.

Would you mind trying something for me?

  1. Could you please add a line to your src/main.ts source file (something like console.log("hello") ) and then
  2. Rebuild your functions with npx nx build firebase
  3. Check if the firebase-admin and firebase-functions deps now appear in your dist package.json

I've seen a wierd Nx issue from time to time, where you have to modify a source file after project generation before Nx will correctly output deps in the package.json.

If that works, then deploy should work also.

Please also let me know what version of Nx you are using, thanks.

simondotm commented 11 months ago

root firebase.json (the only one I have, not an empty one. I don't fully get where to put it)

firebase.json config files always have to be in the root of the workspace to ensure the firebase CLI works properly.

pikilon commented 11 months ago

Hi @simondotm, I found the problem, it is not the package.json dependency. latest firebase changed the API, big time I replaced the main.ts code with this typescript version of the firebase functions example and deploys perfectly

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
import { logger } from 'firebase-functions'
import { onDocumentCreated } from 'firebase-functions/v2/firestore'
import { onRequest } from 'firebase-functions/v2/https'

// The Firebase Admin SDK to access Firestore.
import { initializeApp } from 'firebase-admin/app'
import { getFirestore } from 'firebase-admin/firestore'

initializeApp()

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
export const addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
    .collection('messages')
    .add({ original: original })
  // Send back a message that we've successfully written the message
  res.json({ result: `Message with ID: ${writeResult.id} added.` })
})

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
export const makeuppercase = onDocumentCreated('/messages/{documentId}', (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data?.data().original

  // Access the parameter `{documentId}` with `event.params`
  logger.log('Uppercasing', event.params.documentId, original)

  const uppercase = original.toUpperCase()

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data?.ref.set({ uppercase }, { merge: true })
})

I think the default src/main.ts file should be updated (Maybe with this one, I will be proud to collaborate)

simondotm commented 11 months ago

Ah ok very interesting. Yes, so the boiler plate functions code needs updating in the plugin. Good stuff.

Looks like what is needed is to do a firebase init with firebase CLI and see what the default function code looks like, and copy that into the plugin instead, so that plugin function generator will deliver deployable code on first run. 👍

You are very welcome to submit a PR for this if you like.

pikilon commented 11 months ago

This is the output of firebase init

/**
 * Import function triggers from their respective submodules:
 *
 * import {onCall} from "firebase-functions/v2/https";
 * import {onDocumentWritten} from "firebase-functions/v2/firestore";
 *
 * See a full list of supported triggers at https://firebase.google.com/docs/functions
 */

import {onRequest} from "firebase-functions/v2/https";
import * as logger from "firebase-functions/logger";

// Start writing functions
// https://firebase.google.com/docs/functions/typescript

export const helloWorld = onRequest((request, response) => {
  logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});

I am trying to find the place to make the PR but I can't find it.

simondotm commented 11 months ago

Interesting that the latest CLI function boilerplate doesn't emit any call to initializeApp - I thought that was mandatory, but maybe its not needed for 2nd gen / v2 functions 🤔 (fwiw I'm sticking with 1st gen / v1 on personal projects just now)

The template main.ts file for the function generator is here btw: https://github.com/simondotm/nx-firebase/blob/main/packages/nx-firebase/src/generators/function/files/src/main.ts__tmpl__

Phong6698 commented 11 months ago

Hi @simondotm, I found the problem, it is not the package.json dependency. latest firebase changed the API, big time I replaced the main.ts code with this typescript version of the firebase functions example and deploys perfectly

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
import { logger } from 'firebase-functions'
import { onDocumentCreated } from 'firebase-functions/v2/firestore'
import { onRequest } from 'firebase-functions/v2/https'

// The Firebase Admin SDK to access Firestore.
import { initializeApp } from 'firebase-admin/app'
import { getFirestore } from 'firebase-admin/firestore'

initializeApp()

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
export const addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
    .collection('messages')
    .add({ original: original })
  // Send back a message that we've successfully written the message
  res.json({ result: `Message with ID: ${writeResult.id} added.` })
})

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
export const makeuppercase = onDocumentCreated('/messages/{documentId}', (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data?.data().original

  // Access the parameter `{documentId}` with `event.params`
  logger.log('Uppercasing', event.params.documentId, original)

  const uppercase = original.toUpperCase()

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data?.ref.set({ uppercase }, { merge: true })
})

I think the default src/main.ts file should be updated (Maybe with this one, I will be proud to collaborate)

I have the same issue and I can confirm that this works. But this is the code for firebase functions v2 and I still want to stick to v1 for now 😕

Is it possible that it might have something to do with the build? I really have little experience in that 😅 But what I noticed, compared to 1.1.0-beta.0, is that is uses the @nx/esbuild:esbuild as executor (@simondotm/nx-firebase:build in older version). The generated js file also looks a lot different compared to the old version.

simondotm commented 11 months ago

But this is the code for firebase functions v2 and I still want to stick to v1 for now

I was thinking the same, but reading through the docs, maybe v2 is the way to go now as it feels like v1 support will be withdrawn soon.

https://firebase.google.com/docs/functions/2nd-gen-upgrade

But what I noticed, compared to 1.1.0-beta.0, is that is uses the @nx/esbuild:esbuild as executor (@simondotm/nx-firebase:build in older version). The generated js file also looks a lot different compared to the old version.

Yes v2 of the plugin builds the function code in a completely different but much better way, using esbuild to bundle functions code.

I will look into fixing this template shortly.

elvisun commented 11 months ago

Got this error today took quite a while to figure out the root cause is this, almost decided to not use this library (probably would be a mistake).

Worth doing a new release with the new PR merged?

simondotm commented 10 months ago

@elvisun thanks - and yes I am hoping to find time to do this over next couple of weeks.

simondotm commented 10 months ago

@elvisun - v2.0.0 now released with default firebase function template