Express.js on Cloud Functions for Firebase

Exactly what you would expect, almost…

James Hegedus
codeburst

--

Express.js, or Express, is an unopinionated server-side JavaScript web framework that runs on Node.js. Simply, it provides an API to create and manage HTTP routes, payloads and sessions. It’s one of the most widely used projects by server-side JavaScript developers.

Express on Cloud Functions for Firebase

Why use Express with Cloud Functions?

Running web servers is difficult due to fluctuating traffic as more users request data. Load balancing is the process of routing the traffic to multiple machines to spread the workload, maximizing throughput, minimizing response time and avoiding system overloads. While there are tools available to solve these problems, you must still manage the servers behind the Load Balancer and pay for the unused resources of each machine.

Functions as a Service (FaaS), like Google Cloud Functions, are small server containers that automatically scale as required, this makes it an ideal place to host an API without requiring any specialized knowledge of load balancing. It’s just 3 lines of code in your package.json to deploy your entire auto-scaling server. Just 3 lines! It can also be cheaper since you pay only when a request is being fulfilled, and no more.

Yeah, but why Express?

At this point, you may be asking “Why’re we going to run an Express Server on Cloud Functions. Don’t they already expose an HTTPS endpoint?”.

Well, yes, they do already expose endpoint via HTTPS triggered Cloud Functions, but if we were to use Express, we could use the vast Express middleware ecosystem, or even host our entire API behind a single Cloud Function. Fortunately, the Google developers who implemented Cloud Functions know what they’re doing, and we can simply pass an Express instance directly into the Cloud Function HTTPS handler.

To start, in your functions folder, run yarn add express. Then update the index.js file to be the following:

Express directly in an HTTPS Cloud Function

N.B.: If you wish to use ES6+ syntax as I am here, please read my ES6 in Cloud Functions for Firebase article.

How does this even work?

I asked this question on StackOverflow myself and got a response from a Firebase Engineer:

This all works because under the covers, an Express app is actually just a function that takes a Node.js HTTP request and response and acts on them with some automatic sugaring such as routing. So you can pass an Express router or app to a Cloud Function handler without issue, because Express’s req and res objects are compatible with the standard Node.js versions.

And the Cloud Function handler doesn’t have any issues because as the Cloud Functions for Firebase HTTP Triggers docs outline:

Use functions.https to create a function that handles HTTP events. The event handler for an HTTP function listens for the onRequest() event and supports two HTTP-specific arguments: request and response. These parameters are based off of the Express Request and Response objects, giving you access to their corresponding properties.

So it’s not complete magic, it was just good design. Thanks Google/Firebase engineers!

Using Middleware

One of the most used Express middleware is cors. It’s a small package that enables Cross-Origin Resource Sharing (CORS) with various options. The code for adding middleware is the same here as in any other environment. As an example, we’ll add cors to our server.

Add the package in our ./functionsES6 directory by running:

yarn add cors
npm install cors — as of npm v5 --save is the default behaviour.

Then add cors to our app in ./functionsES6/index.js

Use middleware like you normally would!

Caveats

Trailing Slash “/”

Since we’re using Express you’ll need to add a trailing / to the URL as Express requires. Illustrated below:
us-central1-<project-name>.cloud-functions.net/api
us-central1-<project-name>.cloud-functions.net/api/

If you do not append the slash when calling the Cloud Function, you’ll find you get a 500 error:
Error: could not handle the request

An Automated Solution
Instead of passing in the Express app directly to the Cloud Function, we can just append the / to the request object in the function and return the Express object while passing the new request/response pair through.

This code doesn’t look as clean, and the Cloud Function Express magic is removed, but we have more control and can give a better endpoint to our consumers.

Not as clean, but a better endpoint to consume.

The trailing slash issue aside, there are some other caveats to using an Express server within Cloud Functions, the biggest of which is around BodyParser.

BodyParser

All requests to HTTPS triggered Cloud Functions are automatically parsed by Cloud Functions with an Express Body Parser before the request reaches your code in the function. So even if you define your parser and middleware through Express, it’s still already automatically parsed. And you can’t explicitly define the parser that is used by Cloud Functions, the content-type of the request dictates that, as documented here. Thus, if you do not control the calling environment, you cannot control the parsing of the data.

In addition to this, not all parsing options through Body Parser are available to use even if controlling the calling environment. Again, the options for parsing are available here.

Conclusion

So Express on Cloud Functions is simple! It’s easier to get going than a self-hosted Express server, as it doesn’t require all the knowledge that comes with infrastructure scaling. It’s flexible in that you can use all the technologies and practices you’ve come to expect from the Express ecosystem, assuming you have control over the calling environment.

Thanks to Sam Oliver for his help fleshing out the Body Parser issue.

Need something else to read?

More by me:

If you found this useful, please recommend and share with your friends & colleagues.

--

--

GCP, Firebase, Sveltejs & Deno fan! @jthegedus on all platforms.