Cloud Functions is Google's entry into the serverless computing craze. The promise of serverless architecture is that it dramatically reduces the cost and complexity of developing, scaling, and operating a range of applications. While there are still a host of issues to resolve, the potential benefits are enormous, especially for microservices, IoT, and mobile.
This article and companion github repo show how to use Cloud Functions as the backend for a React app that can be deployed as a static website. While frameworks like Serverless provide more features, such as built-in deployment and a variety of useful plugins, the approach presented here is a simple and quick way to get a basic frontend and backend up and running quickly.
The following sections will walk you through the setup of a simple example that uses Cloud Functions to query an API (the list of articles on oreilly.com), and an accompanying React frontend that will let you page through the data. Figure 1 shows typical output from our simple application.
Figure 1. Sample output from a query to our application
The frontend will also show how to:
- Set up middleware to load data from our backend; it also shows how to display a spinner while the data is being fetched
- Use the material-ui framework to create a super simple UI
- Dispatch events to a web page through the API data
Before you start, you'll need to:
- Create a google cloud account
- Install the Google cloud SDK so that you can manage your cloud from the command line.
- Install (if you don't already have) Node version 6 or higher.
- Install (if you don't already have) create-react-app, Facebook's awesome utility for bootstrapping, developing, and building React projects.
Clone odewahn/cloud-functions-with-react from GitHub,
cdinto it, and run
Once you've got these pieces in place, move on to the next section.
Create a new gcloud project
Google Cloud organizes projects into, well, projects. So the first step is to create a new one. Since this is intended as a basic template to get started, I'll calling it
gcloud projects create react-gcloud-template
This takes about 30-45 seconds. Then you can set it to be the default project for all subsequent
Install and configure the Cloud Functions emulator
The cloud functions emulator allows you to develop and test functions on your local computer, which vastly accelerates development speed.
npm install -g @google-cloud/functions-emulator
Once it's installed, set your new project:
Next, start the emulator:
Finally, deploy the
posts function to the emulator, like this:
functions deploy posts --trigger-http
posts function, which is the user's main entry-point for the example, is defined in the root level file
index.js of the project directory, and uses the node-fetch module to grab a page-by-page list of articles from oreilly.com:
// Get the topic types from ORM site
// Parse the body to get the passed values
// try to parse the body for passed parameters
// provide default values if there is an error
// Wrap function in a CORS header so is can be called in the browser
// Lookup the account type based on the email.
Two small but important things to observe. First, note how the return function is wrapped in a
cors function call; this adds the headers required to get around the browser's cross-origin security restrictions. Without these headers, you'll get this error on any AJAX/fetch call from the browser:
Fetch API cannot load http://localhost:8010/react-gcloud-template/us-central1/posts. No
'Access-Control-Allow-Origin'header is present on the requested resource. Origin
'http://localhost:3000'is therefore not allowed access. If an opaque response serves your needs,
's mode to 'no-cors
'to fetch the resource with CORS disabled.
Second, notice how we're only returning a part of the data we get back (just the values in
data["posts"]). This shows how we can use the Cloud Function to transform a raw API call, which can be valuable when you're developing microservices against a larger API infrastructure.
Here's an example of how to use httpie to call the function from the command line. The value returned is a JSON-formatted array of hashes; each hash contains the data for a post:
http -b http://localhost:8010/react-gcloud-template/us-central1/posts
"Learn how Kotlin functions easily accept default and multiple parameters.",
"Simple functions in Kotlin"
You can also add new functions to
index.js, using posts as a template. Just be sure that you deploy each one individually (i.e.,
functions deploy yourNewFunction --trigger-http). Once deployed, all your changes will be "live" in the emulator each time you update and save the file, so that you'll be executing the new code once you call the function again. This is a huge time savings compared to having to deploy the function directly to Google Cloud, and one of the key strengths of using the
Deploying your function to Google Cloud
Once your function works, you can deploy it to Cloud Functions (versus just the emulator). To do this, you'll need to enable billing and Cloud Functions in the cloud console.
After you've enabled everything, create a cloud storage bucket where your code will be uploaded. (This bucket will also have the
node_modules folder for all your dependencies.) Go into the console and create a bucket where your code can be uploaded to gcloud. Alternatively, you can also use gsutil tool, a command line utility for working with gCloud:
gsutil mb -p react-gcloud-template gs://react-gcloud-template-deploy
In either case, you must use a globally unique bucket name. I called mine
ano-auth-test-bucket. (If you get an error like
AccessDeniedException: 403 The account for bucket "react-gcloud-template-deploy" has been disabled you need to enable billing on the project in the console.)
Remember to run npm
install to download any dependencies. Then you’re ready to deploy your application in the cloud):
gcloud beta functions deploy posts --stage-bucket react-gcloud-template-deploy --trigger-http
That will produce a log like this:
Copying file:///var/folders/hj/x05v_3s544n68fqr31pxsjwr0000gn/T/tmpFaEd0v/fun.zip [Content-Type=application/zip]... / [1 files][ 3.9 MiB/ 3.9 MiB] Operation completed over 1 objects/3.9 MiB. Deploying function (may take a while - up to 2 minutes)...done. availableMemoryMb: 256 entryPoint: posts httpsTrigger: url: https://us-central1-react-gcloud-template.cloudfunctions.net/posts latestOperation: operations/cmVhY3QtZ2Nsb3VkLXRlbXBsYXRlL3VzLWNlbnRyYWwxL3Bvc3RzLzNfQXlBNGR2LUhN name: projects/react-gcloud-template/locations/us-central1/functions/posts serviceAccount: email@example.com sourceArchiveUrl: gs://react-gcloud-template-deploy/us-central1-posts-unmlnlxxrudy.zip status: READY timeout: 60s updateTime: '2017-06-28T14:18:12Z'
httpsTrigger section of the log has the URL for the deployed function:
If you forget this link, you can always recover it like this:
$gcloud beta functions list NAME STATUS TRIGGER posts READY HTTP Trigger
describe it, like so:
$ gcloud beta functions describe posts availableMemoryMb: 256 entryPoint: posts httpsTrigger: url: https://us-central1-react-gcloud-template.cloudfunctions.net/posts latestOperation: operations/cmVhY3QtZ2Nsb3VkLXRlbXBsYXRlL3VzLWNlbnRyYWwxL3Bvc3RzLzNfQXlBNGR2LUhN name: projects/react-gcloud-template/locations/us-central1/functions/posts serviceAccount: firstname.lastname@example.org sourceArchiveUrl: gs://react-gcloud-template-deploy/us-central1-posts-unmlnlxxrudy.zip status: READY timeout: 60s updateTime: '2017-06-28T14:18:12Z'
Set up the React frontend
Once you've set up the backend functions, you're ready to tackle the frontend. If you're starting completely from scratch (as opposed to cloning from an existing project), you'll need create-react-app, a tool from Facebook that claims to let you "create React apps with no build configuration." Basically, it's an opinionated way to structure your projects and tools.
npm install -g create-react-app
If you haven't already, run
npm install to download all your dependencies into the
npm install completes, run
npm start to fire up the app. Note that you may need to modify the URL in
src/state/posts.js to match your functions emulator:
"POST", body: JSON.stringify
From then on, you can make changes in the React app and thanks to the magic of
create-react-app, you'll get immediate hot reloading for your project.
A quick description follows of how the project is organized:
All the frontend code is in the
srcdirectory, which is the default from create-react-app
The main entry point,
src/App.js, sets up the
reduxstore and the redux-thunk middleware. Redux, a library by Dan Abramox, allows you to centralize your application state and all logic that alters that state into one central store, leading to more maintainable code.
src/statedirectory has all the redux stores. I like to use
combine-reducersso that I can break out the state tree into logical units. With that structure, you can easily add a new kind of state objects (for example, a
contentstore with the contents of a post) by putting it in a new file (you can use
src/state/posts.jsas a template) and adding it into the combined reducers in
All the state logic for this sample is in
src/state/posts.js. In addition to the ability to dispatch a thunk that retrieves data from the cloud function, I've also set up logic to control a spinner/progress meter. In production code, you'd want to also do things like handling errors, but that's beyond the scope of this article.
The main UI component is in
src/components/posts.js. This shows how to use the material-ui toolkit to make a simple UI that consists of a list based on the API results, along with a couple of nav buttons to page though the results.
Once you're ready to deploy the React app, you can run
npm run build, upon which
react-create-app will create a compiled, minified app in a build directory that is ready to publish as a static site. For example, you could commit the files in
build to the top level of a
gh-pages branch of a new project and publish your app with GitHub pages.
The combination of the serverless architecture with a React frontend published as a static site is a great way to quickly spin up new applications with minimal administrative overhead. However, serverless really shines for quickly deploying microservices and IoT or mobile backends. The superior economics of cloud functions over traditional hosting present many exciting opportunities, so watch this space carefully.