Creating a MongoDB CRUD backend on Google Cloud Functions.

Ernesto F
Google Cloud - Community
4 min readMay 16, 2017

In the last 2 weeks I have been tinkering with the Cloud Functions from Google. After seeing a great introductory talk and demos on the SF Serverless Meetup by Bret McGowen from Google (https://twitter.com/bretmcg) I decided give it a shoot.

So what are Google Cloud Functions anyway?

Google Cloud Functions is a Functions as a Service (FaaS) offering by Google, billing to the nearest 100 millisecond, running Javascript.

First Google Cloud Functions, Hello World?

This is, of course, a dummy example, but it works!

This is a basic JavaScript module. Exporting a function named handler. This function is very similar to an Expressjs (https://expressjs.com/) handler function. So, anything that works on Express, works here too. (GCF is actually using Expressjs to power their HTTP functions)

GCF has a minimum set of requirements. Like:

  • package.json file to manage dependencies, which will be installed at deploy time.
  • Node 6.9.1 (its a bummer we can’t yet use later versions directly, but Babel can help us here)
  • GCF SDK installed and initialized (https://cloud.google.com/sdk/downloads#interactive)
  • A Google Cloud Storage Bucket to upload or code (Uploading functions from Git is also possible)

The deploy script gets the function from the “main” prop on the package.json file.

{
"name": "gcf-hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

Once we have all the bases covered, we need to create a deploy bucket. A little command will help us there:

$ gsutil mb gs://medium-post-functions

And then, just deploy the function with a “simple” command like:

$ gcloud beta functions deploy hello-world --entry-point handler --trigger-http --stage-bucket medium-post-functions --memory=256MBCopying file:///var/folders/f9/_xttz7rs7t10grd6_q_8s1dr0000gn/T/tmpxV6pSu/fun.zip [Content-Type=application/zip]...
- [1 files][ 438.0 B/ 438.0 B]
Operation completed over 1 objects/438.0 B.
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: handler
httpsTrigger:
url: https://us-central1-revelatio-165320.cloudfunctions.net/hello-world
latestOperation: operations/cmV2ZWxhdGlvLTE2NTMyMC91cy1jZW50cmFsMS9oZWxsby13b3JsZC9nb0pHUXV2TXFTbw
name: projects/revelatio-165320/locations/us-central1/functions/hello-world
serviceAccount: revelatio-165320@appspot.gserviceaccount.com
sourceArchiveUrl: gs://medium-post-functions/us-central1-hello-world-bsesoemdvxwb.zip
status: READY
timeout: 60s
updateTime: '2017-05-15T16:20:11Z'
$

We will get a URL to call our function via HTTP.

$ curl https://us-central1-revelatio-165320.cloudfunctions.net/hello-world
Hello World
$

Adding some dependencies to make it useful

First, I would love to write this functions on a more up to date ES2015 JavaScript. Lets add Babel (https://babeljs.io/) to the mix.

$ yarn add --dev babel-cli babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-1$ yarn add babel-runtime

And a .babelrc file:

{
"presets": ["es2015", "stage-1"],
"plugins": ["transform-runtime"]
}

After this we can also add a script section on our package.json file to make a build of our code (and another to build and deploy)

{
"name": "gcf-hello",
"version": "1.0.0",
"description": "",
"main": "lib/index.js",
"scripts": {
"build": "rm -rf lib/ && `yarn bin`/babel index.js --out-dir ./lib",
"deploy": "yarn build && gcloud beta functions deploy hello-world --entry-point handler --trigger-http --stage-bucket medium-post-functions --memory=256MB"
},

"author": "",
"license": "ISC",
"dependencies": {
"babel-runtime": "^6.23.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-1": "^6.24.1"
}

}

The text marked bold represents the changes to our package.json file.

So now we can write our function with all the nicer ES2015

export const handler = (req, res) => res.send('Hello World')

And now, just:

$ yarn deploy

You can redeploy as many times as you want. The same URL always comes back to use our HTTP function.

Lets make a MongoDB CRUD API backend

First we need to add the mongodb (https://www.npmjs.com/package/mongodb) driver module.

$ yarn add mongodb dotenv

I also added one more dependency here.

I personally prefer to use mLab.com for a hosted mongodb service with a free tier (no need for more that 500Mb), you can have your mongodb DB anywhere, accesible from Internet of course. MongoDB Atlas is another service you could use.

A .env file will help us configure dynamic properties, including the MONGODB connection string. This file should be keep out of versioning control. (Ex. .gitignore)

MONGODB=mongodb://gcf:KjyCaENsTT6q8PVE@ds143231.mlab.com:43231/gcf-hello

Deploy again and test it from the console

$ curl https://us-central1-revelatio-165320.cloudfunctions.net/hello-world[{"_id":"591a25ea734d1d1cd0becda2","name":"Ernesto F.","email":"ernestofreyreg@gmail.com"}]

Now we have the R part from CRUD. Lets try to add the C part (create)

$ yarn deploy

and…

$ curl -d '{"name":"Lucian Freyre", "email":"lucian@codexsw.com"}' -H "Content-Type: application/json" -X POST https://us-central1-revelatio-165320.cloudfunctions.net/hello-world{"result": "ok"}$ curl https://us-central1-revelatio-165320.cloudfunctions.net/hello-world[{"_id":"591a25ea734d1d1cd0becda2","name":"Ernesto F.","email":"ernestofreyreg@gmail.com"},{"_id":"591a2e757b0c5400028ad0c3","name":"Lucian Freyre","email":"lucian@codexsw.com"}]

So far so good, now we have our endpoint working, 2 basic operations (Create and Read). I wont add more ops here, you probably already figured how to add the rest.

Final thoughts

  • FaaS are here to stay.
  • Google Cloud Functions could improve a lot, especially in tooling, documentation and performance.

Next steps

  • GraphQL (http://graphql.org/) of course :) considering we are actually using Expressjs as backend the rest should be fairly easy using the right graphql modules.
  • And server side rendered React.

--

--