ES6 in Cloud Functions for Firebase

Modern language features for modern microservices

James Hegedus
codeburst

--

This post has been deprecated in favour of my new posts about Babel:

  1. Babel & preset-env
    Compile to environments, not specifications
  2. ES6+ in Cloud Functions for Firebase #2
    Babel Boogaloo!

STOP!

This post discussed using Babel to enable use of ES6 in Firebase. This method is now out of date. The above posts describe the preferred method. Save your time and please read those instead.

Welcome to a series exploring Cloud Functions for Firebase with a modern application stack (React, Apollo, GraphQL, Next.js & Firebase). If you are not familiar with this stack, read on! If you are familiar, checkout the TOC above.

ES6 in Cloud Functions for Firebase

Adding ES6 to Cloud Functions

Follow part 1 to get the Cloud Function HelloWorld setup.

Now that we have a basic function, let’s just add some ES6+ code in the way of a template literals and some ES6 module import syntax.

Change the const functions = recquire(“firebase-functions”)to an import statement with aliasing. It’s possible to use named imports as well, but I would advise against it unless you wish to read the source to find what’s exactly exported (eg: https is one).

Pro tip: I use prettier with the --no-semi flag to format my code.

psst, there’s an Atom & VSCode plugin for Prettier

Deploy Time!

That was easy, now let’s deploy that new function. Run firebase deploy from the project root folder.

Fail!

Wait what? Can’t I use ES6 in Cloud Functions? What year is this?

Good question. Well, as it turns out, the cause of that JavaScript fatigue everyone was harping on about in 2016 was the need of many tools to enable us to use modern language features. *mind blown*

Jebus = JS Fatigue = Amazing ecosystem of tools

Most FaaS platforms operate using the Node.js runtime (other languages are available, but Node is usually the first and primary offering) because of JavaScript’s abundant ecosystem of tools and packages. And thank Jebus for that! Because without those tools we’d have to program in ES5. Blegh!

Now it’s not Google’s fault; they support a very current LTS version of NodeJS in Cloud Functions. It’s just that Node.js still doesn’t support all ES6 features completely.

What can we do about this?

Well, as it turns out there’s a tool called Babel that is king of transpiling JavaScript code. Before we get into it though, let’s consider what we want to achieve.

  • develop in ES6+
  • deploy NodeJS compatible code
  • use firebase deploy to deploy to the cloud

Given those requirements we could do either of the following:

  • use the Babel REPL with the ES2015 preset, copy & paste the output overwriting our ES6+. But what do we do if we need to make more changes? This option doesn’t offer any flexibility.
  • we could use Webpack with Babel to watch our code and transpile on-the-fly. But this means adding to a possibly already complex pipeline, and that’s assuming you can edit your Webpack config. Projects bootstrapped with tools like the create-react-app don’t allow easy configuration of Webpack. So this option isn’t very simple.
  • we could use babel-cli to transpile on deploy time and integrate this with our deploy process as a simple npm script.

That last option seems to be the least intrusive and most flexible. So let’s do that.

Folder Structure & NPM packages

To continue to update our ES6 and deploy our transpiled Node compatible code we must have an separate output directory. To leave our Firebase config as is, we need to have our output dir to be the ./functions folder (Firebase can deploy functions from other directories). Thus we must develop our ES6 in a different folder. I suggest ./functionsES6.

Create ./functionsES6 and copy the package.json, yarn.lock and index.js into it from ./functions.

Delete the ./functions folder.

Now let’s setup a package.json in the root folder to house our project new scripts. Run yarn init in the root folder and press enter through each option.

Install Babel and cross-platform ‘rm -rf’ CLI tool Rimraf with yarn add -D babel-cli babel-preset-es2015 rimraf.

NPM Scripts

Now we have the packages and folder structure we need, let’s develop our scripts.

Add a scripts section to your package.json.

You can name the scripts however you like. I like being verbose so I will call mine package-functions.

"package-functions": "babel 'functionsES6' --out-dir 'functions' --presets=es2015 --copy-files --ignore 'node_modules'"

Let’s break this down. Babel will transpile all JavaScript files in the ./functionsES6 directory and output the Node compatible results to the ./functions directory. After transpiling, using the Babel ES2015 preset, a copy of all files Babel didn’t touch are moved to the destination. Finally, it completely ignores any node_modules directory.

Test the script by running yarn run package-functions and have a look at the transpiled code, it should look similar to the original ES5 we had.

If you try and run firebase deploy now you’ll come across an error. This is because they new ./functions directory doesn’t have any node_modules as we never install them after transpiling.

Foiled again!

Since we need to install the packages before deployment we will add a post-script to package-functions. You can read more about npm script hooks here and here.

”postpackage-functions”: “cd functions && yarn”

So after the first script runs it will cd into the ./functions folder and run yarn to install any packages.

Since we want this process to be idempotent we will remove the ./functions directory before each transpile. We will use the pre-script hook for this.

"prepackage-functions": "rimraf functions"

Now when we run package-functions the pre and post scripts are run as you would expect.

Since we want to run this before we run firebase deploy we will concatenate the commands in a deploy script.

"deploy": "yarn run package-functions && firebase deploy"

You should now have a ./package.json that looks like this:

And another at ./functionsES6/package.json that looks like this:

Deploy & Test!

Now we can deploy by simply running yarn deploy at the project root.

Success!

While it does take longer, the added time to transpile is negligible compared to the firebase deploy time, especially when using Yarn or npm5 with their caching abilities!

Now grab the URL out of your CLI or from the Firebase console, and plug it into a browser. If all went well, you should be greeted by the Cloud Function.

It is important to understand how Firebase reads the index.js for functions before deploying. If you look at the transpiled ./functions/index.js you can see the line:

var helloWorld =exports.helloWorld = _firebaseFunctions.https.onRequest(server)

Which is almost the same code Firebase initializes. Firebase simply creates a function for each exports.xyz it can find and names it accordingly.

Conclusion

While inconvenient, it only takes Babel and Rimraf with 3 NPM scripts to get the advantages of thinking and programming in ES6. And I’d argue that the workflow we have afterwards is just as good since it can also easily integrate with any CI/CD we may use in the future.

Now that we can program using ES6+ we have all we need to begin experimenting with the more complex tools of the day, like running an Express Server with GraphQL middleware on Cloud Functions for Firebase.

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.