无服务器
运行无服务器应用程序和REST API,使用您现有的Fastify应用程序。默认情况下,Fastify无法在您选择的无服务器平台上运行,您需要进行一些小的更改来解决此问题。本文档包含针对最流行的无服务器提供商以及如何使用它们与Fastify集成的小指南。
您应该在无服务器平台中使用Fastify吗?
这取决于您!请记住,函数即服务应始终使用小而专注的函数,但您也可以使用它们运行整个 Web 应用程序。重要的是要记住,应用程序越大,初始启动速度就越慢。在无服务器环境中运行 Fastify 应用程序的最佳方法是使用 Google Cloud Run、AWS Fargate 和 Azure 容器实例等平台,服务器可以在其中同时处理多个请求并充分利用 Fastify 的功能。
在无服务器应用程序中使用 Fastify 的最佳功能之一是易于开发。在您的本地环境中,您将始终直接运行 Fastify 应用程序,而无需任何其他工具,而相同的代码将在您选择的无服务器平台中使用额外的代码片段执行。
内容
- AWS
- Google Cloud Functions
- Google Firebase Functions
- Google Cloud Run
- Netlify Lambda
- Platformatic Cloud
- Vercel
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) {
// called directly i.e. "node app"
init().listen({ port: 3000 }, (err) => {
if (err) console.error(err);
console.log('server listening on 3000');
});
} else {
// required as a module => executed on 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
文件并使用 app
作为唯一参数调用导出的 awsLambdaFastify
函数。生成的 proxy
函数具有正确的签名,可用作 lambda handler
函数。这样,所有传入的事件(API Gateway 请求)都将传递到 @fastify/aws-lambda 的 proxy
函数。
示例
可以使用 claudia.js 部署的示例可以在这里找到 这里。
注意事项
- API Gateway 尚未支持流,因此您无法处理 流。
- API Gateway 的超时时间为 29 秒,因此务必在此期间提供回复。
超越 API Gateway
如果您需要与更多 AWS 服务集成,请查看 Fastify 上的 @h4ad/serverless-adapter 以了解如何集成。
Google Cloud Functions
创建 Fastify 实例
const fastify = require("fastify")({
logger: true // you can also define the level passing an object configuration to logger: {level: 'debug'}
});
向 Fastify 实例添加自定义 contentTypeParser
如 问题 #946 中所述,由于 Google Cloud Functions 平台会在请求到达 Fastify 实例之前解析请求的主体,因此在 POST
和 PATCH
方法的情况下会造成请求主体的麻烦,您需要添加一个自定义 Content-Type 解析器
来缓解此行为。
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;
本地测试
安装 适用于 Node.js 的 Google Functions 框架。
您可以全局安装它
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": {
...
"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 作为 Firebase Functions 的 HTTP 框架(而不是 onRequest(async (req, res) => {}
提供的普通 JavaScript 路由器),请遵循本指南。
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 Function 的 HTTP 层已经解析了请求并提供了 JSON 有效负载。它还提供了对原始主体的访问权限(未解析),这对于计算请求签名以验证 HTTP Webhook 很有用。
在 registerRoutes()
函数中添加如下内容
async function registerRoutes (fastify) {
fastify.addContentTypeParser("application/json", {}, (req, payload, done) => {
// useful to include the request's raw body on the `req` object that will
// later be available in your other routes so you can calculate the HMAC
// if needed
req.rawBody = payload.rawBody
// payload.body is already the parsed JSON so we just fire the done callback
// with it
done(null, payload.body)
})
// define your endpoints here...
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
参考文献
- Firebase Functions 上的 Fastify
- 一篇关于 Firebase Functions 和 Fastify 上的 HTTP Webhook 的文章:使用 Lemon Squeezy 的实用案例研究
Google Cloud Run
与 AWS Lambda 或 Google Cloud Functions 不同,Google Cloud Run 是一个无服务器**容器**环境。其主要目的是提供一个基础设施抽象的环境来运行任意容器。因此,Fastify 可以部署到 Google Cloud Run,而无需对您通常编写 Fastify 应用程序的方式进行任何或很少的代码更改。
如果您已熟悉 gcloud,请按照以下步骤部署到 Google Cloud Run,或者只需按照他们的 快速入门指南.
调整 Fastify 服务器
为了使 Fastify 能够在容器内正确监听请求,请确保设置了正确的端口和地址。
function build() {
const fastify = Fastify({ trustProxy: true })
return fastify
}
async function start() {
// Google Cloud Run will set this environment variable for you, so
// you can also use it to detect if you are running in Cloud Run
const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined
// You must listen on the port Cloud Run provides
const port = process.env.PORT || 3000
// You must listen on all IPV4 addresses in Cloud Run
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 应用程序。可以在官方的 gcloud 文档 中找到一个基本的 Dockerfile
。
# Use the official Node.js 10 image.
# https://hub.docker.com/_/node
FROM node:10
# Create and change to the app directory.
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure both package.json AND package-lock.json are copied.
# Copying this separately prevents re-running npm install on every code change.
COPY package*.json ./
# Install production dependencies.
RUN npm i --production
# Copy local code to the container image.
COPY . .
# Run the web service on container startup.
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
的文件夹,然后在 functions
文件夹内创建 server.js
(您的端点路径将为 server.js
)。
functions/server.js
export { handler } from '../lambda.js'; // Change `lambda.js` path to your `lambda.js` path
netlify.toml
[build]
# This will be run the site build
command = "npm run build:functions"
# This is the directory is publishing to netlify's CDN
# and this is directory of your front of your app
# publish = "build"
# functions build directory
functions = "functions-build" # always appends `-build` folder to your `functions` folder for builds
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": {
...
"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"
...
}
然后它应该可以正常工作。
Platformatic Cloud
Platformatic 为 Node.js 应用程序提供零配置部署。要立即使用它,您应该将现有的 Fastify 应用程序包装在 Platformatic 服务 中,方法是运行以下命令。
npm create platformatic@latest -- service
向导会要求您填写一些答案。
? Where would you like to create your project? .
? Do you want to run npm install? yes
? Do you want to use TypeScript? no
? What port do you want to use? 3042
[13:04:14] INFO: Configuration file platformatic.service.json successfully created.
[13:04:14] INFO: Environment file .env successfully created.
[13:04:14] INFO: Plugins folder "plugins" successfully created.
[13:04:14] INFO: Routes folder "routes" successfully created.
? Do you want to create the github action to deploy this application to Platformatic Cloud dynamic workspace? no
? Do you want to create the github action to deploy this application to Platformatic Cloud static workspace? no
然后,前往 Platformatic Cloud 并使用您的 GitHub 帐户登录。创建您的第一个应用程序和一个静态工作区:请务必将 API 密钥下载为 env 文件,例如 yourworkspace.txt
。
然后,您可以使用以下命令轻松部署您的应用程序。
platformatic deploy --keys `yourworkspace.txt`
查看 完整指南,了解如何在 Platformatic 中包装 Fastify 应用程序。
Vercel
Vercel 为 Node.js 应用程序提供零配置部署。要立即使用它,只需像下面这样配置您的 vercel.json
文件即可。
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/api/serverless.js"
}
]
}
然后,编写如下所示的 api/serverless.js
。
"use strict";
// Read the .env file.
import * as dotenv from "dotenv";
dotenv.config();
// Require the framework
import Fastify from "fastify";
// Instantiate Fastify with some config
const app = Fastify({
logger: true,
});
// Register your application as a normal plugin.
app.register(import("../src/app.js"));
export default async (req, res) => {
await app.ready();
app.server.emit('request', req, res);
}
在 src/app.js
中定义插件。
async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
export default routes;