UNPKG

7.86 kBMarkdownView Raw
1<h1 align="center">Serverless</h1>
2
3Run serverless applications and REST APIs using your existing Fastify application.
4
5### Contents
6
7- [AWS Lambda](#aws-lambda)
8- [Google Cloud Run](#google-cloud-run)
9- [Zeit Now](#zeit-now)
10
11### Attention Readers:
12> Fastify is not designed to run on serverless environments.
13The Fastify framework is designed to make implementing a traditional HTTP/S server easy.
14Serverless environments requests differently than a standard HTTP/S server;
15thus, we cannot guarantee it will work as expected with Fastify.
16Regardless, based on the examples given in this document,
17it is possible to use Fastify in a serverless environment.
18Again, keep in mind that this is not Fastify's intended use case and
19we do not test for such integration scenarios.
20
21## AWS Lambda
22
23The sample provided allows you to easily build serverless web applications/services
24and RESTful APIs using Fastify on top of AWS Lambda and Amazon API Gateway.
25
26*Note: Using [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify) is just one possible way.*
27
28### app.js
29
30```js
31const fastify = require('fastify');
32
33function init() {
34 const app = fastify();
35 app.get('/', (request, reply) => reply.send({ hello: 'world' }));
36 return app;
37}
38
39if (require.main === module) {
40 // called directly i.e. "node app"
41 init().listen(3000, (err) => {
42 if (err) console.error(err);
43 console.log('server listening on 3000');
44 });
45} else {
46 // required as a module => executed on aws lambda
47 module.exports = init;
48}
49```
50
51When executed in your lambda function we don't need to listen to a specific port,
52so we just export the wrapper function `init` in this case.
53The [`lambda.js`](https://www.fastify.io/docs/latest/Serverless/#lambda-js) file will use this export.
54
55When you execute your Fastify application like always,
56i.e. `node app.js` *(the detection for this could be `require.main === module`)*,
57you can normally listen to your port, so you can still run your Fastify function locally.
58
59### lambda.js
60
61```js
62const awsLambdaFastify = require('aws-lambda-fastify')
63const init = require('./app');
64
65const proxy = awsLambdaFastify(init())
66// or
67// const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] })
68
69exports.handler = proxy;
70// or
71// exports.handler = (event, context, callback) => proxy(event, context, callback);
72// or
73// exports.handler = (event, context) => proxy(event, context);
74// or
75// exports.handler = async (event, context) => proxy(event, context);
76```
77
78We just require [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify)
79(make sure you install the dependency `npm i --save aws-lambda-fastify`) and our
80[`app.js`](https://www.fastify.io/docs/latest/Serverless/#app-js) file and call the
81exported `awsLambdaFastify` function with the `app` as the only parameter.
82The resulting `proxy` function has the correct signature to be used as lambda `handler` function.
83This way all the incoming events (API Gateway requests) are passed to the `proxy` function of [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify).
84
85### Example
86
87An example deployable with [claudia.js](https://claudiajs.com/tutorials/serverless-express.html) can be found [here](https://github.com/claudiajs/example-projects/tree/master/fastify-app-lambda).
88
89
90### Considerations
91
92- API Gateway doesn't support streams yet, so you're not able to handle [streams](https://www.fastify.io/docs/latest/Reply/#streams).
93- API Gateway has a timeout of 29 seconds, so it's important to provide a reply during this time.
94
95## Google Cloud Run
96
97Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless **container** environment. It's primary purpose is to provide an infrastructure-abstracted environment to run arbitrary containers. As a result, Fastify can be deployed to Google Cloud Run with little-to-no code changes from the way you would write your Fastify app normally.
98
99*Follow the steps below to deploy to Google Cloud Run if you are already familiar with gcloud or just follow their [quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*.
100
101### Adjust Fastify server
102
103In order for Fastify to properly listen for requests within the container, be sure to set the correct port and address:
104
105```js
106function build() {
107 const fastify = Fastify({ trustProxy: true })
108 return fastify
109}
110
111async function start() {
112 // Google Cloud Run will set this environment variable for you, so
113 // you can also use it to detect if you are running in Cloud Run
114 const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined
115
116 // You must listen on the port Cloud Run provides
117 const port = process.env.PORT || 3000
118
119 // You must listen on all IPV4 addresses in Cloud Run
120 const address = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined
121
122 try {
123 const server = build()
124 const address = await server.listen(port, address)
125 console.log(`Listening on ${address}`)
126 } catch (err) {
127 console.error(err)
128 process.exit(1)
129 }
130}
131
132module.exports = build
133
134if (require.main === module) {
135 start()
136}
137```
138
139### Add a Dockerfile
140
141You can add any valid `Dockerfile` that packages and runs a Node app. A basic `Dockerfile` can be found in the official [gcloud docs](https://github.com/knative/docs/blob/2d654d1fd6311750cc57187a86253c52f273d924/docs/serving/samples/hello-world/helloworld-nodejs/Dockerfile).
142
143```Dockerfile
144# Use the official Node.js 10 image.
145# https://hub.docker.com/_/node
146FROM node:10
147
148# Create and change to the app directory.
149WORKDIR /usr/src/app
150
151# Copy application dependency manifests to the container image.
152# A wildcard is used to ensure both package.json AND package-lock.json are copied.
153# Copying this separately prevents re-running npm install on every code change.
154COPY package*.json ./
155
156# Install production dependencies.
157RUN npm install --only=production
158
159# Copy local code to the container image.
160COPY . .
161
162# Run the web service on container startup.
163CMD [ "npm", "start" ]
164```
165
166### Add a .dockerignore
167
168To keep build artifacts out of your container (which keeps it small and improves build times), add a `.dockerignore` file like the one below:
169
170```.dockerignore
171Dockerfile
172README.md
173node_modules
174npm-debug.log
175```
176
177### Submit build
178
179Next, submit your app to be built into a Docker image by running the following command (replacing `PROJECT-ID` and `APP-NAME` with your GCP project id and an app name):
180
181```bash
182gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME
183```
184
185### Deploy Image
186
187After your image has built, you can deploy it with the following command:
188
189```bash
190gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed
191```
192
193Your app will be accessible from the URL GCP provides.
194
195## Zeit Now
196
197[now](https://zeit.co/home) provides zero configuration deployment for
198Node.js applications. In order to use now, it is as simple as
199configuring your `now.json` file like the following:
200
201```json
202{
203 "version": 2,
204 "builds": [
205 {
206 "src": "api/serverless.js",
207 "use": "@now/node",
208 "config": {
209 "helpers": false
210 }
211 }
212 ],
213 "routes": [
214 { "src": "/.*", "dest": "/api/serverless.js"}
215 ]
216}
217```
218
219Then, write a `api/serverless.js` like so:
220
221```js
222'use strict'
223
224const build = require('./index')
225
226const app = build()
227
228module.exports = async function (req, res) {
229 await app.ready()
230 app.server.emit('request', req, res)
231}
232```
233
234And a `api/index.js` file:
235
236```js
237'use strict'
238
239const fastify = require('fastify')
240
241function build () {
242 const app = fastify({
243 logger: true
244 })
245
246 app.get('/', async (req, res) => {
247 const { name = 'World' } = req.query
248 req.log.info({ name }, 'hello world!')
249 return `Hello ${name}!`
250 })
251
252 return app
253}
254
255module.exports = build
256```
257
258Note that you'll need to use Node 10 by setting it in `package.json`:
259
260```js
261 "engines": {
262 "node": "10.x"
263 },
264```