Serverless

Serverless from the ground up: Building a simple microservice with Cloud Functions (Part 1)

Do your company’s employees rely on multiple scattered systems and siloed data to do their jobs? Do you wish you could easily stitch all these systems together, but can’t figure out how? Turns out there’s an easy way to combine these different systems in a way that’s useful, easy to create and easy to maintain—it’s called serverless microservices, and if you can code in Javascript, you can integrate enterprise software systems.

Today, we’re going to show you an easy way to build a custom content management system using Google Cloud Functions, our serverless event-driven framework that easily integrates with a variety of standard tools, APIs and Google products. We’ll teach by example, following Alice through her workday, and watch how a lunchtime conversation with Bob morphs into a custom document repository for their company. You could probably use a similar solution in your own organization, but may not know just how easy it can be, or where to start.

version 1.png

Alice and Bob both work at Blueprint Mobile—a fictional company that sells and repairs mobile phones. At lunch one day, Bob tells Alice how his entire morning was lost searching for a specific device manual. This is hardly surprising, since the company relies on documents scattered across Team and personal Drive folders, old file servers, and original manufacturer websites. Alice’s mind races to an image of a perfect world, where every manual is discoverable from a single link, and she convinces Bob to spend the afternoon seeing what they could build.

Alice’s idea is to create a URL that lists all the documents, and lets technicians use a simple web app to find the right one. Back at her desk, she pings Carol, the company’s intranet developer, to sanity-check her idea and see if it will work with the company intranet. With Carol’s help, Alice and Bob settle on this architecture:

New microservice integration.png
New microservice integration with existing systems

The group gathers to brainstorm, where they decide that a microservice called handsetdocs should return a JSON array where each element is a document, belonging to a handset. They sketch out this JSON structure on a whiteboard:

  [
  {
    "handset": "GS-98",
    "name": "GS-98 spare parts",
    "url": "https://docs.google.com/document/..."
  },
  {
    "handset": "GS-98",
    "name": "Tech bulletin 3A",
    "url": "https://example.com/bulletins/3a.html"
  },
  ...
]

Then, they decide that Bob will build a repair shop web app that will interact with Alice’s microservice like this:

Sequence diagram.png
Sequence diagram for the new microservice and web app

Alice takes a photo of the whiteboard and goes back to her desk. She starts her code editor and implements the handsetdocs microservice using Cloud Functions:

  exports.handsetdocs = (req, res) => {
  handleCors(req, res);
  res.status(200).type('text/json').end(JSON.stringify(getHandsetDocs()));
};

This is the first code that runs when the service’s URL is accessed. The first line sets up Cross-Origin Resource Sharing (CORS), which we’ll explain in more detail later. The second line of the function calls getHandsetDocs() and returns the response to the caller. (Alice also took a note to look into IAM security later, to make sure that only her colleagues are able to access her microservice.)


Alice deploys the getHandsetDocs() function in the same file as handsetdocs above. Her first draft of the function is a simple hard-coded list of documents:

  function getHandsetDocs() {
  return [
    {'handset': 'GS-98', name: 'GS-98 spare parts', url: '...'},
    {'handset': 'GS-98', name: 'Tech bulletin 3A', url: '...'},
    {'handset': 'GS-98', name: 'Tear-down instructions', url: '...'},
    {'handset': 'Cobra', name: 'Cobra spare parts', url: '...'},
    {'handset': 'Cobra', name: 'Tech bulletin Aug 2018', url: '...'},
    {'handset': 'Cobra', name: 'Tear-down instructions', url: '...'},
    {'handset': 'Digit4', name: 'Digit4 spare parts', url: '...'},
    {'handset': 'Digit4', name: 'Tech bulletin Sep 2018', url: '...'},
    {'handset': 'Digit4', name: 'Tear-down instructions', url: '...'},
  ];
}

Finally, Alice reads up on Cross-Origin Resource Sharing (CORS) in Cloud Functions. CORS allows applications running on one domain to access content from another domain. This will let Bob and Carol write web pages that run on the company’s internal domain but make requests to the handsetdocs microservice running on cloudfunctions.net, Cloud Functions’ default hosting domain. You can read more about CORS at MDN.

  handleCors = (req, res) => {
  res.set("Access-Control-Allow-Origin", "*");
  res.set("Access-Control-Allow-Methods", "GET");
  res.set("Access-Control-Allow-Headers", "Content-Type");
  res.set("Access-Control-Max-Age", "3600");
  if (req.method == 'OPTIONS') {
    res.status(204).send('');
  }
}

Alice puts all three functions above in a file called index.js and deploys the handsetdocs microservice as a cloud function:

  gcloud functions deploy handsetdocs --trigger-http --runtime=nodejs8

The code in getHandsetDocs() won’t win any prizes, but it allows Bob and Carol to start testing their web apps (which call the handsetdocs microservice) within an hour of their discussion.


Bob takes advantage of this useful microservice and writes the first version of the repair store web app. Its HTML consists of two empty lists. The first list will display all the handsets. When you click an entry in that list, the second list will be populated with all the documents for that handset.

web app’s user interface.gif
The web app’s user interface
  <html>
  <head>
    <title>
      Handset documents
    </title>
    <script type="text/javascript" src="script.js"></script>
  </head>
  <body>
    <h1>Handsets</h1>
    <ul id="handset_list">
    </ul>
    <hr/>
    <h1>Documents</h1>
    <ul id="doc_list">
    </ul>
  </body>
</html>

To populate each list, calls to the handsetdocs microservice are done from the app’s Javascript file. When the web page first loads, it hits the microservice to get the list of docs and lists the unique handset names in the first list of the page. When the user clicks a handset name, the docs keyed to that handset are displayed in the second list on the page.

  const URL_BASE = 'https://<REGION><PROJECT ID>.cloudfunctions.net/';
const HANDSET_URL = URL_BASE + 'handsetdocs';

let docs;

fetch(HANDSET_URL).then(function(response) {
  return response.json();
}).then(function(responseJson) {
  docs = responseJson;
  const handsetNames = [...new Set(docs.map(doc => doc.handset))];
  let ul = document.querySelector('#handset_list');
  handsetNames.forEach(handsetName => {
    let li = document.createElement("li");
    let a = document.createElement("a");
    a.setAttribute('href', '#');
    a.onclick = () => { populateDocList(handsetName) };
    a.appendChild(document.createTextNode(handsetName));
    li.appendChild(a);
    ul.appendChild(li);
  })
});

function populateDocList(handsetName) {
  let ul = document.querySelector('#doc_list');
  ul.innerHTML = '';
  docs.forEach(doc => {
    if (doc.handset == handsetName) {
      let li = document.createElement("li");
      let a = document.createElement("a");
      a.setAttribute('href', doc.url);
      a.appendChild(document.createTextNode(doc.name));
      li.appendChild(a);
      ul.appendChild(li);
    }
  })
}

This code solves a real business need in a fairly simple manner—Alice calls it version 0.1. The technicians now have a single source of truth for documents. Also, Carol can call the microservice from her intranet app to publish a document list that all other employees can access. In one afternoon, Alice and Bob have hopefully prevented any more lost mornings spent hunting down the right document!

Over the next few days, Bob continues to bring Alice lists of documents for the various handsets. For each new document, Alice adds a row to the getHandsetDocs() function and deploys the new version. These quick updates allow them to grow their reference list each time someone discovers another useful document. Since Blueprint Mobile only sells a small number of handsets, this isn’t too much of a burden on either Bob or Alice. But what happens if there’s a sudden surge of documents to bring into the system?

Click over to Part 2, where Alice and Bob use Google Sheets to enable other departments to use the system.