使用现有函数将 Next.js 应用部署到 Firebase Functions

人气:782 发布:2022-10-16 标签: firebase google-cloud-functions next.js

问题描述

遵循一些教程 像这个,我能够将我的 Next.js 应用程序部署到 Firebase 托管/功能.这是我的项目的简化文件夹结构:

Following a few tutorials like this one, I was able to deploy my Next.js app to Firebase Hosting/Functions. Here's the reduced folder structure of my project:

myApp
  node_modules // Next.js app dependencies
  .next // destination of npm run build of the Next.js project
  pages // Next.js pages
  functions
    lib // this is where the functions compile
    node_modules // functions' dependencies
    src
      app.ts
      index.ts
      other.ts
    package.json
  public // Next.js app public files
  firebase.json
  package.json
  ...

当我运行 firebase deploy 时,它会将 functions 编译到它的 lib 文件夹中并成功部署所有内容.

When I run firebase deploy, it compiles functions into its lib folder and deploys everything successfully.

但是,当我尝试访问我的网络应用程序的 url 时,我得到 Error: could not handle the request.当我查看 Firebase Functions 日志时,我可以看到一条错误提示 Error: Cannot find module 'react'.因此,我再次成功地将 react 添加到 functions/package.jsonnpm installfirebase deploy.但是,另一个错误,但这次它说 Cannot find module 'react-dom'.

However, when I try to visit the url of my web app, I get Error: could not handle the request. When I looked into the Firebase Functions log, I can see an error saying Error: Cannot find module 'react'. So, I added react to functions/package.json, npm install and firebase deploy successfully again. However, another error, but this time it says Cannot find module 'react-dom'.

我的理解是,这样做的原因是我的 Next.js 应用程序依赖于我的 root 文件夹中的 package.json 中列出的所有依赖项(减少了下面列出):

My understanding is that the reason for this is that my Next.js app relies on all the dependencies listed in my package.json in my root folder (a reduced list below):

"dependencies": {
    "babel-plugin-prismjs": "^2.1.0",
    "firebase": "^8.8.1",
    "firebase-admin": "^9.11.0",
    "formik": "^2.2.9",
    "js-cookie": "^3.0.0",
    "next": "11.0.1",
    "next-i18next": "^8.5.5",
    "nookies": "^2.5.2",
    "prismjs": "^1.24.1",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-ga": "^3.3.0",
    "react-select": "^4.3.1",
    "react-spinners": "^0.11.0",
    "react-transition-group": "^4.4.2",
    "recharts": "^2.0.10",
    "styled-components": "^5.3.0",
    "yup": "^0.32.9"
  }

而我的 functions/package.json 显然要短得多:

while my functions/package.json was, obviously, much shorter:

"dependencies": {
    "@sendgrid/mail": "^7.4.4",
    "firebase-admin": "^9.8.0",
    "firebase-functions": "^3.14.1",
    "next": "^11.0.1",
    "react": "^17.0.2"
  }

我假设错误会要求我将所有依赖项复制到 functions/package.json 文件中,这显然是我不想要的.

I assume the errors would have asked me to duplicate all the dependencies into the functions/package.json file, which I obviously don't want.

或者,我可以将 firebase-functionsfunctions/package.json 中的其他相关依赖项添加到 root/package.json,并且在根文件夹中也有下一个服务器设置.但是,如何包含其他现有的云功能?

Alternatively, I can add firebase-functions and other relevant dependencies from functions/package.json to root/package.json, and have the next server setup in the root folder as well. However, how do I include the other existing cloud functions?

我的 functions/src 文件的内容如下所示:

Here's how the contents of my functions/src files look:

index.ts

import * as admin from 'firebase-admin'

admin.initializeApp()

export * from './app' // next.js
export * from './other'

other.ts

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

exports.onUserCreate = functions.database.ref...
// my other cloud functions, such as db triggers, https functions, etc.

app.ts

import next from 'next'
import {https} from 'firebase-functions'

const server = next({
  dev: process.env.NODE_ENV !== 'production',
  conf: {distDir: '.next'}
})

const nextjsHandler = server.getRequestHandler()

exports.app = https.onRequest(async (req, res) => {
  await server.prepare()
  return await nextjsHandler(req, res)
})

另外,这是我的 firebase.json:

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

如何正确部署 Next.js 应用并确保我的其他云功能继续工作?

How do I deploy the Next.js app correctly and make sure my other cloud functions continue to work?

谢谢!

推荐答案

虽然您可以将 /functions 目录提升到项目的根目录,但这意味着您的其他函数不必要地臃肿Next.js 应用程序的依赖项.

While you could lift the /functions directory to your project's root directory, this would mean that your other functions are unnecessarily bloated with all of your Next.js app's dependencies.

当您部署云函数时,配置的部署目录(默认为 /functions)中的所有内容都会被部署 - 即使您不使用它.它也为每个功能部署一次.因此,如果您的函数目录中有一个 10MB 的文件,则每个函数的部署大小将增加 10MB.这就是我所说的腹胀".一个函数.

When you deploy a Cloud Function, everything in the configured deployment directory (/functions by default) is deployed - even if you don't use it. It is also deployed once for each function. So if you had a 10MB file to your functions directory, every function's deployed size would increase by 10MB. This is what I mean by "bloating" a function.

仅供参考:您可以通过将以下内容添加到您的 firebase.json 文件(docs):

FYI: You can change the deployed functions directory by adding the following to your firebase.json file (docs):

"functions": {
  "source": "." // <- the project's root directory
}

相反,您应该使用部分部署.在这种情况下,您需要将 app.ts 移动到 Next 项目的目录中,并将其他不依赖于 Next 部署的函数留在 /functions 文件夹中.如果您部署整个项目目录,即使您不使用它,您最终也会部署 /functions,因此您应该将 Next 前端和函数移动到它自己的文件夹中.

Instead, you should make use of partial deployment. In this situation you'd move app.ts to your Next project's directory and leave the other functions that don't depend on your Next deployment in the /functions folder. If you deploy your entire project directory, you will end up deploying /functions too even though you don't use it, so you should instead move your Next front-end and functions into its own folder.

文件结构类似于(详情如下):

The file structure will look similar to (details below):

/myApp
  /functions
    /lib            // this is where the functions compile
    /node_modules   // functions' dependencies
    /src
      index.ts
      other.ts
    firebase.json   // configuration for this folder's contents
    package.json    // configuration for this folder's contents
  /next
    /node_modules   // Next.js app dependencies
    /.next          // compiled Next.js project
    /pages          // Next.js pages
    /public         // Next.js app public files
    /functions-lib
      app.js        // compiled Next function
    /functions-src
      app.ts        // source of Next function
    firebase.json   // configuration for this folder's contents
    package.json    // configuration for this folder's contents
    ...

您的 /functions 文件夹的 firebase.json 文件类似于:

Your /functions folder's firebase.json file would look similar to:

{
  "functions": {
    "source": ".",
    "predeploy": [
      "npm --prefix "$RESOURCE_DIR" run lint",
      "npm --prefix "$RESOURCE_DIR" run build"
    ]
  }
}

要使上述结构正常运行,您需要调整 firebase.jsonpackage.json 文件的配置.

To get the above structure to behave properly, you need to tweak the configuration of the firebase.json and package.json files.

将以下内容添加到 /function 文件夹的 package.json 文件中:

Add the following to the /function folder's package.json file:

"scripts": {
  "deploy": "firebase deploy --only functions:otherFunc1,functions:otherFunc2",
  ...
}

您也可以将这些函数导出为单个组以便于部署,但函数将命名为 otherFunctions-otherFunc1otherFunctions-otherFunc2 等:

You could also export these functions as a single group for easy deployment, but the functions will be named like otherFunctions-otherFunc1, otherFunctions-otherFunc2, and so on:

// index.ts
import * as otherFunctions from './other.ts';
export { otherFunctions };

// package.json
"scripts": {
  "deploy": "firebase deploy --only functions:otherFunctions",
  ...
}

您的 /next 文件夹的 firebase.json 文件类似于:

Your /next folder's firebase.json file would look similar to:

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

将以下(或类似内容)添加到 /next 文件夹的 package.json 文件中:

Add the following (or similar) to the /next folder's package.json file:

"scripts": {
  "build": "npm run build:next && npm run build:functions",
  "build:functions": "tsc",
  "build:next": "next build",
  "lint": "npm run lint:next && npm run lint:functions",
  "lint:functions": "eslint --ext .js,.ts ./functions-src",
  "lint:next": "...",
  "deploy": "firebase deploy --only hosting,functions:app"
}

通过上述更改,放入适当的目录并在使用 firebase deploy 时使用 npm run deploy.

With the above changes, drop into the appropriate directory and use npm run deploy whenever you would use firebase deploy.

注意:不要忘记 Cloud Functions 自动使用请求的主体,因此在 Next 项目中使用主体解析器并使用 next start 进行本地测试时,请确保考虑到这一点.

Note: Don't forget that Cloud Functions consume the bodies of requests automatically, so make sure you account for this when using body parsers in Next projects and testing locally with next start.

548