CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
Serverless.md662 linesDownload Raw Back to Guides
1<h1 align="center">Serverless</h1>2 3Run serverless applications and REST APIs using your existing Fastify4application. By default, Fastify will not work on your serverless platform of5choice, you will need to make some small changes to fix this. This document6contains a small guide for the most popular serverless providers and how to use7Fastify with them.8 9#### Should you use Fastify in a serverless platform?10 11That is up to you! Keep in mind that functions as a service should always use12small and focused functions, but you can also run an entire web application with13them. It is important to remember that the bigger the application the slower the14initial boot will be. The best way to run Fastify applications in serverless15environments is to use platforms like Google Cloud Run, AWS Fargate, and Azure16Container Instances, where the server can handle multiple requests at the same17time and make full use of Fastify's features.18 19One of the best features of using Fastify in serverless applications is the ease20of development. In your local environment, you will always run the Fastify21application directly without the need for any additional tools, while the same22code will be executed in your serverless platform of choice with an additional23snippet of code.24 25### Contents26 27- [AWS](#aws)28- [Google Cloud Functions](#google-cloud-functions)29- [Google Firebase Functions](#google-firebase-functions)30- [Google Cloud Run](#google-cloud-run)31- [Netlify Lambda](#netlify-lambda)32- [Platformatic Cloud](#platformatic-cloud)33- [Vercel](#vercel)34 35## AWS36 37To integrate with AWS, you have two choices of library:38 39- Using [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify)40  which only adds API Gateway support but has heavy optimizations for fastify.41- Using [@h4ad/serverless-adapter](https://github.com/H4ad/serverless-adapter)42  which is a little slower as it creates an HTTP request for each AWS event but43  has support for more AWS services such as: AWS SQS, AWS SNS and others.44 45So you can decide which option is best for you, but you can test both libraries.46 47### Using @fastify/aws-lambda48 49The sample provided allows you to easily build serverless web50applications/services and RESTful APIs using Fastify on top of AWS Lambda and51Amazon API Gateway.52 53#### app.js54 55```js56const fastify = require('fastify');57 58function init() {59  const app = fastify();60  app.get('/', (request, reply) => reply.send({ hello: 'world' }));61  return app;62}63 64if (require.main === module) {65  // called directly i.e. "node app"66  init().listen({ port: 3000 }, (err) => {67    if (err) console.error(err);68    console.log('server listening on 3000');69  });70} else {71  // required as a module => executed on aws lambda72  module.exports = init;73}74```75 76When executed in your lambda function we do not need to listen to a specific77port, so we just export the wrapper function `init` in this case. The78[`lambda.js`](#lambdajs) file will use this export.79 80When you execute your Fastify application like always, i.e. `node app.js` *(the81detection for this could be `require.main === module`)*, you can normally listen82to your port, so you can still run your Fastify function locally.83 84#### lambda.js85 86```js87const awsLambdaFastify = require('@fastify/aws-lambda')88const init = require('./app');89 90const proxy = awsLambdaFastify(init())91// or92// const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] })93 94exports.handler = proxy;95// or96// exports.handler = (event, context, callback) => proxy(event, context, callback);97// or98// exports.handler = (event, context) => proxy(event, context);99// or100// exports.handler = async (event, context) => proxy(event, context);101```102 103We just require104[@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify) (make sure105you install the dependency `npm i @fastify/aws-lambda`) and our106[`app.js`](#appjs) file and call the exported `awsLambdaFastify` function with107the `app` as the only parameter. The resulting `proxy` function has the correct108signature to be used as a lambda `handler` function. This way all the incoming109events (API Gateway requests) are passed to the `proxy` function of110[@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify).111 112#### Example113 114An example deployable with115[claudia.js](https://claudiajs.com/tutorials/serverless-express.html) can be116found117[here](https://github.com/claudiajs/example-projects/tree/master/fastify-app-lambda).118 119### Considerations120 121- API Gateway does not support streams yet, so you are not able to handle122  [streams](../Reference/Reply.md#streams).123- API Gateway has a timeout of 29 seconds, so it is important to provide a reply124  during this time.125 126#### Beyond API Gateway127 128If you need to integrate with more AWS services, take a look at129[@h4ad/serverless-adapter](https://viniciusl.com.br/serverless-adapter/docs/main/frameworks/fastify)130on Fastify to find out how to integrate.131 132## Google Cloud Functions133 134### Creation of Fastify instance135```js136const fastify = require("fastify")({137  logger: true // you can also define the level passing an object configuration to logger: {level: 'debug'}138});139```140 141### Add Custom `contentTypeParser` to Fastify instance142 143As explained [in issue144#946](https://github.com/fastify/fastify/issues/946#issuecomment-766319521),145since the Google Cloud Functions platform parses the body of the request before146it arrives at the Fastify instance, troubling the body request in case of `POST`147and `PATCH` methods, you need to add a custom [`Content-Type148Parser`](../Reference/ContentTypeParser.md) to mitigate this behavior.149 150```js151fastify.addContentTypeParser('application/json', {}, (req, body, done) => {152  done(null, body.body);153});154```155 156### Define your endpoint (examples)157 158A simple `GET` endpoint:159```js160fastify.get('/', async (request, reply) => {161  reply.send({message: 'Hello World!'})162})163```164 165Or a more complete `POST` endpoint with schema validation:166```js167fastify.route({168  method: 'POST',169  url: '/hello',170  schema: {171    body: {172      type: 'object',173      properties: {174        name: { type: 'string'}175      },176      required: ['name']177    },178    response: {179      200: {180        type: 'object',181        properties: {182          message: {type: 'string'}183        }184      }185    },186  },187  handler: async (request, reply) => {188    const { name } = request.body;189    reply.code(200).send({190      message: `Hello ${name}!`191    })192  }193})194```195 196### Implement and export the function197 198Final step, implement the function to handle the request and pass it to Fastify199by emitting `request` event to `fastify.server`:200 201```js202const fastifyFunction = async (request, reply) => {203  await fastify.ready();204  fastify.server.emit('request', request, reply)205}206 207exports.fastifyFunction = fastifyFunction;208```209 210### Local test211 212Install [Google Functions Framework for213Node.js](https://github.com/GoogleCloudPlatform/functions-framework-nodejs).214 215You can install it globally:216```bash217npm i -g @google-cloud/functions-framework218```219 220Or as a development library:221```bash222npm i -D @google-cloud/functions-framework223```224 225Then you can run your function locally with Functions Framework:226```bash227npx @google-cloud/functions-framework --target=fastifyFunction228```229 230Or add this command to your `package.json` scripts:231```json232"scripts": {233...234"dev": "npx @google-cloud/functions-framework --target=fastifyFunction"235...236}237```238and run it with `npm run dev`.239 240 241### Deploy242```bash243gcloud functions deploy fastifyFunction \244--runtime nodejs14 --trigger-http --region $GOOGLE_REGION --allow-unauthenticated245```246 247#### Read logs248```bash249gcloud functions logs read250```251 252#### Example request to `/hello` endpoint253```bash254curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \255  -H "Content-Type: application/json" \256  -d '{ "name": "Fastify" }'257{"message":"Hello Fastify!"}258```259 260### References261- [Google Cloud Functions - Node.js Quickstart262  ](https://cloud.google.com/functions/docs/quickstart-nodejs)263 264## Google Firebase Functions265 266Follow this guide if you want to use Fastify as the HTTP framework for267Firebase Functions instead of the vanilla JavaScript router provided with268`onRequest(async (req, res) => {}`.269 270### The onRequest() handler271 272We use the `onRequest` function to wrap our Fastify application instance.273 274As such, we'll begin with importing it to the code:275 276```js277const { onRequest } = require("firebase-functions/v2/https")278```279 280### Creation of Fastify instance281 282Create the Fastify instance and encapsulate the returned application instance283in a function which will register routes, await the server's processing of284plugins, hooks and other settings. As follows:285 286```js287const fastify = require("fastify")({288  logger: true,289})290 291const fastifyApp = async (request, reply) => {292  await registerRoutes(fastify)293  await fastify.ready()294  fastify.server.emit("request", request, reply)295}296```297 298### Add Custom `contentTypeParser` to Fastify instance and define endpoints299 300Firebase Function's HTTP layer already parses the request301and makes a JSON payload available. It also provides access302to the raw body, unparsed, which is useful in order to calculate303request signatures to validate HTTP webhooks.304 305Add as follows to the `registerRoutes()` function:306 307```js308async function registerRoutes (fastify) {309  fastify.addContentTypeParser("application/json", {}, (req, payload, done) => {310    // useful to include the request's raw body on the `req` object that will311    // later be available in your other routes so you can calculate the HMAC312    // if needed313    req.rawBody = payload.rawBody314 315    // payload.body is already the parsed JSON so we just fire the done callback316    // with it317    done(null, payload.body)318  })319 320  // define your endpoints here...321  fastify.post("/some-route-here", async (request, reply) => {}322 323  fastify.get('/', async (request, reply) => {324    reply.send({message: 'Hello World!'})325  })326}327```328 329### Export the function using Firebase onRequest330 331Final step is to export the Fastify app instance to Firebase's own332`onRequest()` function so it can pass the request and reply objects to it:333 334```js335exports.app = onRequest(fastifyApp)336```337 338### Local test339 340Install the Firebase tools functions so you can use the CLI:341 342```bash343npm i -g firebase-tools344```345 346Then you can run your function locally with:347 348```bash349firebase emulators:start --only functions350```351 352### Deploy353 354Deploy your Firebase Functions with:355 356```bash357firebase deploy --only functions358```359 360#### Read logs361 362Use the Firebase tools CLI:363 364```bash365firebase functions:log366```367 368### References369- [Fastify on Firebase Functions](https://github.com/lirantal/lemon-squeezy-firebase-webhook-fastify/blob/main/package.json)370- [An article about HTTP webhooks on Firebase Functions and Fastify: A Practical Case Study with Lemon Squeezy](https://lirantal.com/blog/http-webhooks-firebase-functions-fastify-practical-case-study-lemon-squeezy)371 372 373## Google Cloud Run374 375Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless376**container** environment. Its primary purpose is to provide an377infrastructure-abstracted environment to run arbitrary containers. As a result,378Fastify can be deployed to Google Cloud Run with little-to-no code changes from379the way you would write your Fastify app normally.380 381*Follow the steps below to deploy to Google Cloud Run if you are already382familiar with gcloud or just follow their383[quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*.384 385### Adjust Fastify server386 387In order for Fastify to properly listen for requests within the container, be388sure to set the correct port and address:389 390```js391function build() {392  const fastify = Fastify({ trustProxy: true })393  return fastify394}395 396async function start() {397  // Google Cloud Run will set this environment variable for you, so398  // you can also use it to detect if you are running in Cloud Run399  const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined400 401  // You must listen on the port Cloud Run provides402  const port = process.env.PORT || 3000403 404  // You must listen on all IPV4 addresses in Cloud Run405  const host = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined406 407  try {408    const server = build()409    const address = await server.listen({ port, host })410    console.log(`Listening on ${address}`)411  } catch (err) {412    console.error(err)413    process.exit(1)414  }415}416 417module.exports = build418 419if (require.main === module) {420  start()421}422```423 424### Add a Dockerfile425 426You can add any valid `Dockerfile` that packages and runs a Node app. A basic427`Dockerfile` can be found in the official [gcloud428docs](https://github.com/knative/docs/blob/2d654d1fd6311750cc57187a86253c52f273d924/docs/serving/samples/hello-world/helloworld-nodejs/Dockerfile).429 430```Dockerfile431# Use the official Node.js 10 image.432# https://hub.docker.com/_/node433FROM node:10434 435# Create and change to the app directory.436WORKDIR /usr/src/app437 438# Copy application dependency manifests to the container image.439# A wildcard is used to ensure both package.json AND package-lock.json are copied.440# Copying this separately prevents re-running npm install on every code change.441COPY package*.json ./442 443# Install production dependencies.444RUN npm i --production445 446# Copy local code to the container image.447COPY . .448 449# Run the web service on container startup.450CMD [ "npm", "start" ]451```452 453### Add a .dockerignore454 455To keep build artifacts out of your container (which keeps it small and improves456build times) add a `.dockerignore` file like the one below:457 458```.dockerignore459Dockerfile460README.md461node_modules462npm-debug.log463```464 465### Submit build466 467Next, submit your app to be built into a Docker image by running the following468command (replacing `PROJECT-ID` and `APP-NAME` with your GCP project id and an469app name):470 471```bash472gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME473```474 475### Deploy Image476 477After your image has built, you can deploy it with the following command:478 479```bash480gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed481```482 483Your app will be accessible from the URL GCP provides.484 485 486## netlify-lambda487 488First, please perform all preparation steps related to **AWS Lambda**.489 490Create a folder called `functions`,  then create `server.js` (and your endpoint491path will be `server.js`) inside the `functions` folder.492 493### functions/server.js494 495```js496export { handler } from '../lambda.js'; // Change `lambda.js` path to your `lambda.js` path497```498 499### netlify.toml500 501```toml502[build]503  # This will be run the site build504  command = "npm run build:functions"505  # This is the directory is publishing to netlify's CDN506  # and this is directory of your front of your app507  # publish = "build"508  # functions build directory509  functions = "functions-build" # always appends `-build` folder to your `functions` folder for builds510```511 512### webpack.config.netlify.js513 514**Do not forget to add this Webpack config, or else problems may occur**515 516```js517const nodeExternals = require('webpack-node-externals');518const dotenv = require('dotenv-safe');519const webpack = require('webpack');520 521const env = process.env.NODE_ENV || 'production';522const dev = env === 'development';523 524if (dev) {525  dotenv.config({ allowEmptyValues: true });526}527 528module.exports = {529  mode: env,530  devtool: dev ? 'eval-source-map' : 'none',531  externals: [nodeExternals()],532  devServer: {533    proxy: {534      '/.netlify': {535        target: 'http://localhost:9000',536        pathRewrite: { '^/.netlify/functions': '' }537      }538    }539  },540  module: {541    rules: []542  },543  plugins: [544    new webpack.DefinePlugin({545      'process.env.APP_ROOT_PATH': JSON.stringify('/'),546      'process.env.NETLIFY_ENV': true,547      'process.env.CONTEXT': env548    })549  ]550};551```552 553### Scripts554 555Add this command to your `package.json` *scripts*556 557```json558"scripts": {559...560"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"561...562}563```564 565Then it should work fine566 567## Platformatic Cloud568 569[Platformatic](https://platformatic.dev) provides zero-configuration deployment570for Node.js applications.571To use it now, you should wrap your existing Fastify application inside a572[Platformatic Service](https://oss.platformatic.dev/docs/reference/service/introduction),573by running the following:574 575 576```bash577npm create platformatic@latest -- service578```579 580The wizard would ask you to fill in a few answers:581 582```583? Where would you like to create your project? .584? Do you want to run npm install? yes585? Do you want to use TypeScript? no586? What port do you want to use? 3042587[13:04:14] INFO: Configuration file platformatic.service.json successfully created.588[13:04:14] INFO: Environment file .env successfully created.589[13:04:14] INFO: Plugins folder "plugins" successfully created.590[13:04:14] INFO: Routes folder "routes" successfully created.591? Do you want to create the github action to deploy this application to Platformatic Cloud dynamic workspace? no592? Do you want to create the github action to deploy this application to Platformatic Cloud static workspace? no593```594 595Then, head to [Platformatic Cloud](https://platformatic.cloud) and sign in596with your GitHub account.597Create your first application and a static workspace: be careful to download the598API key as an env file, e.g. `yourworkspace.txt`.599 600Then, you can easily deploy your application with the following command:601 602```bash603platformatic deploy --keys `yourworkspace.txt`604```605 606Check out the [Full Guide](https://blog.platformatic.dev/how-to-migrate-a-fastify-app-to-platformatic-service)607on how to wrap Fastify application in Platformatic.608 609## Vercel610 611[Vercel](https://vercel.com) provides zero-configuration deployment for Node.js612applications. To use it now, it is as simple as configuring your `vercel.json`613file like the following:614 615```json616{617    "rewrites": [618        {619            "source": "/(.*)",620            "destination": "/api/serverless.js"621        }622    ]623}624```625 626Then, write `api/serverless.js` like so:627 628```js629"use strict";630 631// Read the .env file.632import * as dotenv from "dotenv";633dotenv.config();634 635// Require the framework636import Fastify from "fastify";637 638// Instantiate Fastify with some config639const app = Fastify({640  logger: true,641});642 643// Register your application as a normal plugin.644app.register(import("../src/app.js"));645 646export default async (req, res) => {647    await app.ready();648    app.server.emit('request', req, res);649}650```651 652In `src/app.js` define the plugin.653```js654async function routes (fastify, options) {655  fastify.get('/', async (request, reply) => {656    return { hello: 'world' }657  })658}659 660export default routes;661```662