Firebase部署Node.js 16云函数失败 加载用户代码报错
Firebase Functions 部署报错:用户代码加载失败
问题描述
- 部署Firebase Functions过程中抛出错误,提示函数加载用户代码失败,系统判定为用户代码存在bug
- 本地启动
firebase emulators模拟器时,API可以正常接收请求并返回结果,但正式部署完成后所有API都无法访问 - 手动逐行检查代码未发现明显语法错误,尝试补充admin服务账号初始化配置后,部署到Node.js 16运行时的
api函数步骤仍然抛出相同错误 - 部署日志核心输出如下:
[info] + functions: functions folder uploaded successfully [info] i functions: updating Node.js 16 function api(us-central1)... [error] Function failed on loading user code. This is likely due to a bug in the user code. [debug] [2022-06-11T11:35:57.021Z] Total Function Deployment time: 58448 [debug] [2022-06-11T11:35:57.021Z] 1 Functions Deployed [debug] [2022-06-11T11:35:57.021Z] 1 Functions Errored
关联代码
函数入口实现
const functions = require("firebase-functions"); const express=require("express"); const cors=require("cors"); require('dotenv').config({ path: './.env' }); // 初始化Stripe客户端 const stripeAPI = require('stripe')(process.env.REACT_APP_SECRET_KEY); // 初始化Express应用 const app=express(); var admin = require("firebase-admin"); var serviceAccount = require("../serviceAccountKey.json"); admin.initializeApp({ credential: admin.credential.cert(serviceAccount) }); // 注册中间件 app.use(cors({origin: true})); app.use(express.json()); async function createCheckoutSession(req, res) { const domainUrl = process.env.WEB_APP_URL; const { line_items, customer_email } = req.body; if (!line_items || !customer_email) { return res.status(400).json({ error: 'missing required session parameters' }); } let session; try { session = await stripeAPI.checkout.sessions.create({ payment_method_types: ['card'], mode: 'payment', line_items, customer_email, success_url: `${domainUrl}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${domainUrl}/canceled`, shipping_address_collection: { allowed_countries: ['GB', 'US'] } }); res.status(200).json({ sessionId: session.id, }); } catch (error) { console.log(error); res.status(400).json({ error: 'an error occured, unable to create session'}); } } app.get('/',(req, res)=>res.send('Hello World!')); app.post('/create-checkout-session', createCheckoutSession); exports.api=functions.https.onRequest(app);
functions目录package.json配置
{ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "lint": "eslint .", "serve": "firebase emulators:start --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "engines": { "node": "16" }, "main": "index.js", "dependencies": { "cors": "^2.8.5", "express": "^4.17.2", "firebase-admin": "^9.8.0", "firebase-functions": "^3.21.2", "stripe": "^8.200.0" }, "devDependencies": { "dotenv": "^16.0.1", "eslint": "^7.6.0", "eslint-config-google": "^0.14.0", "firebase-functions-test": "^0.2.0", "nodemon": "^2.0.16" }, "private": true }
问题原因与修复方法
本地运行正常但线上部署加载失败,全部是本地环境和Cloud Functions线上运行环境的差异导致,一共三个触发点:
- 依赖配置错误:
dotenv被放在了devDependencies节点下,Cloud Functions部署时默认不会安装开发依赖,代码执行到require('dotenv')时直接抛出模块不存在的错误,函数在冷启动加载阶段就直接崩溃。修复方式是执行npm install dotenv --save,把dotenv移到生产依赖列表中。 - 服务账号文件引用错误:代码中加载的
../serviceAccountKey.json是本地存放在项目根目录的密钥文件,Cloud Functions部署时只会打包上传functions目录内的文件,上层目录的密钥文件不会被上传,运行时加载这个路径会直接报文件不存在。实际上线环境不需要手动传入服务账号密钥,直接调用admin.initializeApp()即可,Cloud Functions运行时会自动注入当前项目的默认服务账号凭证,不需要手动上传密钥文件。本地开发时可以加环境变量判断,仅本地环境加载本地密钥文件即可。 - 环境变量读取逻辑错误:本地通过dotenv加载
.env文件的逻辑在线上不生效,Cloud Functions不会读取本地的.env文件,代码中用到的REACT_APP_SECRET_KEY、WEB_APP_URL在线上环境都是undefined,会导致Stripe客户端初始化失败。修复方式是通过Firebase CLI配置线上环境变量,执行如下命令:
代码中对应读取环境变量的部分,改成firebase functions:config:set stripe.secret_key="你的Stripe正式密钥" web.app_url="你的线上站点访问地址"functions.config().stripe.secret_key和functions.config().web.app_url即可。本地开发时可以执行firebase functions:config:get > .runtimeconfig.json把线上配置拉到本地,模拟器会自动读取这个配置文件,不需要依赖dotenv也能正常运行。
内容的提问来源于stack exchange,提问作者Grace
相关产品推荐
相关产品推荐

