无服务器
使用现有的Fastify应用运行无服务器应用程序和REST API。您可能需要对代码进行更改,以适应所选的无服务器平台。本文档包含最受欢迎的无服务器提供商的小型指南以及如何与它们一起使用Fastify。
您是否应该在无服务器平台上使用Fastify?
这取决于您!请记住,作为服务的功能应始终使用小型且专注的功能,但您也可以用它们来运行整个Web应用程序。重要的是要记得,应用越大,初始启动速度越慢。在无服务器环境中运行Fastify应用的最佳方式是使用Google Cloud Run、AWS Fargate、Azure容器实例和Vercel等平台,在这些平台上服务器可以同时处理多个请求并充分利用Fastify的功能。
在无服务器应用中使用Fastify的一个最佳特性就是开发的简便性。在本地环境,您将直接运行Fastify应用而无需任何额外工具,而在所选的无服务器平台上,相同的代码只需添加一小段代码即可执行。
目录
AWS
要与AWS集成,您有两个库选择:
- 使用 @fastify/aws-lambda ,该库仅添加API Gateway支持,但针对fastify进行了大量优化。
- 使用 @h4ad/serverless-adapter ,该库略慢一些,因为它为每个AWS事件创建一个HTTP请求,但它支持更多AWS服务,例如:AWS SQS、AWS SNS等。
因此您可以根据需要选择最佳选项,但可以测试这两个库。
使用 @fastify/aws-lambda
提供的示例允许您轻松地使用Fastify在AWS Lambda和Amazon API Gateway之上构建无服务器Web应用程序和服务以及RESTful API。
app.js
const fastify = require('fastify');
function init() {
const app = fastify();
app.get('/', (request, reply) => reply.send({ hello: 'world' }));
return app;
}
if (require.main === module) {
// 直接调用,例如 "node app"
init().listen({ port: 3000 }, (err) => {
if (err) console.error(err);
console.log('服务器在端口3000上监听');
});
} else {
// 作为模块导入时,在AWS Lambda中执行
module.exports = init;
}
当在Lambda函数中执行时,我们不需要监听特定的端口,因此在这种情况下导出包装器函数 init
。文件 lambda.js
将使用此导出。
当你像往常一样执行你的Fastify应用程序(例如 node app.js
)*(检测可以是 require.main === module
)*时,你可以正常监听端口,因此你仍然可以在本地运行你的Fastify函数。
lambda.js
const awsLambdaFastify = require('@fastify/aws-lambda')
const init = require('./app');
const proxy = awsLambdaFastify(init())
// or
// const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] })
exports.handler = proxy;
// or
// exports.handler = (event, context, callback) => proxy(event, context, callback);
// or
// exports.handler = (event, context) => proxy(event, context);
// or
// exports.handler = async (event, context) => proxy(event, context);
我们只需要引入
@fastify/aws-lambda (确保安装依赖项 npm i @fastify/aws-lambda
)和我们的
app.js
文件,并将导出的 awsLambdaFastify
函数与应用实例作为唯一参数调用。生成的 proxy
函数具有正确的签名,可以作为 lambda 处理函数使用。这样所有传入事件(API Gateway 请求)都会传递给
@fastify/aws-lambda 的 proxy
函数。
示例
一个可部署的示例可以在 claudia.js 中找到,地址为 此处 。
考虑事项
- API Gateway 目前不支持流,因此您无法处理 流。
- API Gateway 的超时时间为29秒,在这段时间内必须提供响应。
API Gateway之外
如果您需要与更多AWS服务集成,请查看 Fastify 上的 @h4ad/serverless-adapter 以了解如何进行集成。
Genezio
Genezio 是一个旨在简化将无服务器应用程序部署到云平台的平台。
Genezio 提供了专门用于部署 Fastify 应用程序的指南。
Google Cloud Functions
创建Fastify实例
const fastify = require("fastify")({
logger: true // 您也可以通过传递一个配置对象来定义日志级别:{level: 'debug'}
});
向Fastify实例添加自定义contentTypeParser
如issue #946 中所述,由于Google Cloud Functions平台在请求到达Fastify实例之前解析请求体,在POST
和PATCH
方法的情况下可能会导致问题,因此您需要添加一个自定义的Content-Type Parser
来缓解这种行为。
fastify.addContentTypeParser('application/json', {}, (req, body, done) => {
done(null, body.body);
});
定义您的端点(示例)
一个简单的GET
端点:
fastify.get('/', async (request, reply) => {
reply.send({message: 'Hello World!'})
})
或者一个更完整的带有模式验证的POST
端点:
fastify.route({
method: 'POST',
url: '/hello',
schema: {
body: {
type: 'object',
properties: {
name: { type: 'string'}
},
required: ['name']
},
response: {
200: {
type: 'object',
properties: {
message: {type: 'string'}
}
}
},
},
handler: async (request, reply) => {
const { name } = request.body;
reply.code(200).send({
message: `Hello ${name}!`
})
}
})
实现并导出函数
最后一步,实现处理请求的函数,并通过向fastify.server
发出request
事件将其传递给Fastify:
const fastifyFunction = async (request, reply) => {
await fastify.ready();
fastify.server.emit('request', request, reply)
}
exports.fastifyFunction = fastifyFunction;
本地测试
安装Google Functions Framework for Node.js 。
你可以全局安装它:
npm i -g @google-cloud/functions-framework
或者作为开发库安装:
npm i -D @google-cloud/functions-framework
然后,你可以在本地使用 Functions Framework 运行你的函数:
npx @google-cloud/functions-framework --target=fastifyFunction
或者将此命令添加到 package.json
的 scripts 中:
"scripts": {
...
"dev": "npx @google-cloud/functions-framework --target=fastifyFunction"
...
}
并使用 npm run dev
运行它。
部署
gcloud functions deploy fastifyFunction \
--runtime nodejs14 --trigger-http --region $GOOGLE_REGION --allow-unauthenticated
查看日志
gcloud functions logs read
示例请求到 /hello
端点
curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \
-H "Content-Type: application/json" \
-d '{ "name": "Fastify" }'
{"message":"Hello Fastify!"}
参考资料
Google Firebase Functions
如果您希望使用 Fastify 替换默认的纯 JavaScript 路由器作为 HTTP 框架来实现 Firebase 函数,请参考本指南。
onRequest() 处理程序
我们使用 onRequest
函数来包装我们的 Fastify 应用实例。
因此,我们将从导入该函数开始:
const { onRequest } = require("firebase-functions/v2/https")
创建 Fastify 实例
创建 Fastify 实例,并将其返回的应用实例封装在一个注册路由、等待插件、钩子和其他设置处理的函数中。如下所示:
const fastify = require("fastify")({
logger: true,
})
const fastifyApp = async (request, reply) => {
await registerRoutes(fastify)
await fastify.ready()
fastify.server.emit("request", request, reply)
}
向 Fastify 实例添加自定义 contentTypeParser
并定义端点
Firebase 函数的 HTTP 层已经解析了请求,并提供了 JSON 负载。它还提供了原始未解析的主体访问,这对于计算验证 HTTP Webhook 的签名非常有用。
在 registerRoutes()
函数中按如下方式添加:
async function registerRoutes (fastify) {
fastify.addContentTypeParser("application/json", {}, (req, payload, done) => {
// 将请求的原始主体包含到稍后可以在其他路由中使用的 `req` 对象上,以便在需要时计算 HMAC。
req.rawBody = payload.rawBody
// payload.body 已经是解析后的 JSON,因此我们只需使用它来调用 done 回调
done(null, payload.body)
})
// 在这里定义您的端点...
fastify.post("/some-route-here", async (request, reply) => {})
fastify.get('/', async (request, reply) => {
reply.send({message: 'Hello World!'})
})
}
使用Firebase onRequest导出函数
最后一步是将Fastify应用实例导出到Firebase自己的onRequest()
函数中,以便它可以传递请求和回复对象:
exports.app = onRequest(fastifyApp)
本地测试
安装Firebase工具以使用CLI:
npm i -g firebase-tools
然后你可以通过以下命令在本地运行你的函数:
firebase emulators:start --only functions
部署
使用以下命令部署你的Firebase Functions:
firebase deploy --only functions
查看日志
使用Firebase工具CLI:
firebase functions:log
参考资料
Google Cloud Run
与 AWS Lambda 或 Google Cloud Functions 不同,Google Cloud Run 是一个无服务器的 容器 环境。其主要目的是提供一种抽象基础设施来运行任意容器。因此,Fastify 可以在几乎不需要任何代码更改的情况下部署到 Google Cloud Run。
如果您已经熟悉 gcloud,请按照以下步骤进行部署,或者直接遵循他们的 快速入门指南 。
调整 Fastify 服务器
为了让 Fastify 在容器中正确监听请求,请确保设置正确的端口和地址:
function build() {
const fastify = Fastify({ trustProxy: true })
return fastify
}
async function start() {
// Google Cloud Run 将为您设置此环境变量,因此您可以使用它来检测是否在 Cloud Run 中运行
const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined
// 您必须监听 Cloud Run 提供的端口
const port = process.env.PORT || 3000
// 在 Cloud Run 中您必须监听所有 IPV4 地址
const host = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined
try {
const server = build()
const address = await server.listen({ port, host })
console.log(`Listening on ${address}`)
} catch (err) {
console.error(err)
process.exit(1)
}
}
module.exports = build
if (require.main === module) {
start()
}
添加 Dockerfile
您可以添加任何有效的 Dockerfile
来打包和运行 Node 应用程序。一个基本的 Dockerfile
可以在官方 gcloud 文档 中找到。
# 使用官方 Node.js 10 镜像。
# https://hub.docker.com/_/node
FROM node:10
# 创建并切换到应用目录。
WORKDIR /usr/src/app
# 将应用程序依赖项清单复制到容器镜像中。
# 使用通配符确保同时复制 package.json 和 package-lock.json 文件。
# 单独复制这些文件可以防止在每次代码更改时重新运行 npm install。
COPY package*.json ./
# 安装生产环境依赖项。
RUN npm i --production
# 将本地代码复制到容器镜像中。
COPY . .
# 在容器启动时运行 Web 服务。
CMD [ "npm", "start" ]
添加 .dockerignore 文件
为了将构建生成的文件排除在容器之外(这可以使容器更小,并提高构建速度),请添加如下的 .dockerignore
文件:
Dockerfile
README.md
node_modules
npm-debug.log
提交构建
接下来,通过运行以下命令提交您的应用以构建为 Docker 镜像(将 PROJECT-ID
和 APP-NAME
替换为您在 GCP 中的项目 ID 和应用名称):
gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME
部署镜像
构建完成后,您可以使用以下命令部署该镜像:
gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed
您的应用将可以通过 GCP 提供的 URL 访问。
netlify-lambda
首先,请完成所有与 AWS Lambda 相关的准备工作。
创建一个名为 functions
的文件夹,然后在该文件夹内创建 server.js
(你的端点路径将是 server.js
)。
functions/server.js
export { handler } from '../lambda.js'; // 将 `lambda.js` 路径改为你的 `lambda.js` 路径
netlify.toml
[build]
# 这将在构建站点时运行
command = "npm run build:functions"
# 此目录将发布到 Netlify 的 CDN,并且是应用前端的目录
# publish = "build"
# 函数构建目录
functions = "functions-build" # 构建时始终在你的 `functions` 文件夹后添加 `-build` 文件夹
webpack.config.netlify.js
不要忘记添加此 Webpack 配置,否则可能会出现问题
const nodeExternals = require('webpack-node-externals');
const dotenv = require('dotenv-safe');
const webpack = require('webpack');
const env = process.env.NODE_ENV || 'production';
const dev = env === 'development';
if (dev) {
dotenv.config({ allowEmptyValues: true });
}
module.exports = {
mode: env,
devtool: dev ? 'eval-source-map' : 'none',
externals: [nodeExternals()],
devServer: {
proxy: {
'/.netlify': {
target: 'http://localhost:9000',
pathRewrite: { '^/.netlify/functions': '' }
}
}
},
module: {
rules: []
},
plugins: [
new webpack.DefinePlugin({
'process.env.APP_ROOT_PATH': JSON.stringify('/'),
'process.env.NETLIFY_ENV': true,
'process.env.CONTEXT': env
})
]
};
脚本
将此命令添加到你的 package.json
scripts 中
"scripts": {
...
"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"
...
}
然后应该可以正常工作。
Vercel
Vercel 完全支持部署 Fastify 应用程序。 此外,借助 Vercel 的 Fluid 计算 ,您可以结合使用类似服务器的并发性以及传统无服务器函数的自动扩展特性。