Using Fastify on Google Cloud Run
A closer look at using HTTP/2, WebSockets and Server Sent Events with Fastify on Google Cloud Run
Google recently announced end-to-end HTTP/2, WebSockets and streaming support in their Cloud Run service offering. This is great news because our projects often rely on cloud architectures, and being able to use these features natively opens up scenarios that were not possible until now.
Fastify is NearForm’s web framework of choice and it has supported these features for a long time, but when running in the cloud it needs the service provider to support them.
This blog post will show you how to use Cloud Run to run Fastify applications that use HTTP/2, WebSockets and streaming of Server Sent Events. The source code accompanying this post is available at nearform/fastify-cloud-run .
For a primer about using HTTP/2 in Node.js, we recommend watching James Snell’s Having fun with HTTP/2 in Node.js .
HTTP/2
Cloud Run has always supported HTTP/2. Until now, all HTTP/2 connections were downgraded to HTTP/1 when they were sent to a container running your Cloud Run code.
The recent release of Cloud Run introduced end-to-end support for HTTP/2, meaning that we can fully leverage Fastify support for HTTP/2 when running in Cloud Run.
Below is a simple Fastify application to illustrate this. It can also be found in the repository accompanying this blog post.
The main difference between a non-HTTP/2 application and the above example is the use of the http2 flag
in the server option. Setting it to true enables HTTP/2 support in the server.
Running the example locally
To run this application locally, simply execute:
The server will start listening on port 3000, and you can check it works by making a request to the server via curl, for example:
We force curl to assume the server supports HTTP/2 using the --http2-prior-knowledge
flag.
The response will look similar to the following:
Because the application’s code included Node’s raw request httpVersion property, we can be sure that HTTP/2 was used to handle the request.
Running the example in the cloud
To run the application on Cloud Run you must open a Google Cloud Shell and execute the following commands:
We use the beta version of the gcloud program because the new features are not yet available in the stable release. Also, we explicitly enable support for HTTP/2 by using the --use-http2 flag.
You may need to provide additional input to prompts that Cloud Shell will output on the screen, or additional arguments to the command, depending on how your Cloud Shell is configured. A common mandatory argument is the Google Cloud project ID, which is specified using the --project
argument and it needs to have billing enabled.
Cloud Shell will carry out a few steps to prepare the environment to run the application. It will upload the application’s source code to Cloud Run, it will build it, create a container inside which to run it, create a revision for the application and finally start serving traffic to it. When all the steps are complete, Cloud Shell will output a URL which can be used to send requests to the application.
We can then use the application’s url to send a request to it in the same way we did with the local version of the application.
Because the Cloud Run version of the application is running over HTTPS, we can also use a browser to send requests to it. Most browsers support HTTP/2, but only when the server is running over HTTPS, which wasn’t the case with the locally running application. Don’t forget to remove the fastify-http2 service from Cloud Run to avoid further billing. You can do so via the Cloud Run Console.
WebSockets
WebSockets are another useful feature that Cloud Run didn’t support until recently. Fastify supports WebSockets via the fastify-websocket plugin . A simple WebSocket echo server written with Fastify is shown below:
When it receives a message via WebSocket, this server will echo it back to the sender.
We can test it out locally by running the application:
We then connect to it with any WebSocket client. We used websocat
in the example below:
Note the use of the ws:// scheme to access the server via Websocket. Typing any text and pressing ENTER will send the message to the server over WebSocket, and the server will echo it back.
Running this in Cloud Run is very similar to the HTTP/2 example, with the exception that WebSockets don’t run on HTTP/2 but over HTTP/1:
To test it, simply change the URL provided to the websocat command. For example:
Note the use of the wss:// scheme, as the WebSocket example runs over secure WebSocket in Cloud Run, which is the same as HTTPS but for WebSockets instead of HTTP.
Server Sent Events
Google Cloud Run now enables streaming via Server Sent Events over HTTP/2 , which are a mechanism that web servers can use to push messages to clients.
Server Sent Events existed and worked over HTTP/1 too, but they had limitations that were removed in HTTP/2. The example we show uses SSE over HTTP/2.
An example of using SSE over HTTP/2 with Fastify is shown below:
When it receives an HTTP request, this simple application starts a timer that sends a SSE containing the current server date back to the client approximately every second. When the client connection is closed, the timer is stopped so we don’t leak memory, and we close the response stream.
Once the application starts, we can then make a request to it:
The -N flag disables buffering of the response stream because we want to see the data as soon as the server sends it to us, without any buffering. This application can be run in Cloud Run in the same way as for the HTTP/2 and WebSocket examples:
And tested in the same way:
A whiteboard with Fastify over WebSocket
We used WebSockets to create a fun whiteboard demo. Fastify can be used as a static web server via fastify-static and with WebSockets via the fastify-websocket plugin . A simple way to broadcast the messages is to get the list of WebSocket clients, iterate through them and check that the current connection client is not the same as one of the client’s.
The code above avoids delivering the same message to the sender by filtering the WebSockets client list and excluding the sending connection from the list of recipients.
Running this in Cloud Run over HTTP/1:
We can test it out locally by running the application:
The application starts at port 3000 by default if no PORT environment variable is present. The picture below shows it in action using two browser windows to collaborate on the same whiteboard:
The picture below shows an example of a piece of artistic work done with the whiteboard:
This post was cowritten with Paul Isache.
Insight, imagination and expertly engineered solutions to accelerate and sustain progress.
Contact