Home Serverless
Serverless
Cancel

Serverless

Learning Outcomes

  • outline the problems associated with scaling the backend of a web application
  • implenent a serverless web application using Google Cloud Functions

Resources

Lab

Video of this lab

Set Up

Fork https://gitlab.com/langarabrian/gcpfunc2. Remove the fork relationship. Make the project private. Clone the project in your Google Cloud Shell environment.

Finish Backend

The backend has a feathers mongoose service books for managing a collection of books. We are going to deploy to Google Cloud Functions. Feathers comes with a transport for Express which have been for previous assignments. There is no transport for Google Cloud Functions, so we are going to have marshal the incoming requests to invoke the correct Feathers service method.

Change to the gcpfunc2/backend directory. Run npm install.

Set your MONGODBURI environment variable. Run the backend with the PORT=8080 npm run startff command. startff is a script which runs Functions Framework. The Functions Framework allows you test at Google Cloud Function locally without having to deploy it. Verify that there are no errors in the console. Use Web Preview to preview the application in a new tab. There should just be message that the request is unsupported.

The first thing we need to do is support the creation of new books in the collection.

Rewrite the exports.feathers function in backend\src\index.js as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Set up Google Cloud Functions entry point
exports.feathers = async (req, res) => {
  // for CORS
  res.set('Access-Control-Allow-Origin', '*');

  if ( req.method === 'OPTIONS') {
    // Send response to OPTIONS requests for CORS
    res.set('Access-Control-Allow-Headers', 'Content-Type');
    res.status(204).send('');

  } else if ( req.method === 'POST' && req.path === '/books') {
    const books = app.service('books');
    const result = await books.create( req.body );
    res.json( result );

  } else {
    console.log( "method: " + req.method );
    console.log( "path: " + req.path );
    res.send('Unsupported request');

  }
};

Because the frontend will be running a domain separate from the Google Cloud Function, we need to configure the function to support cross-origin resource sharing (CORS).

1
res.set('Access-Control-Allow-Origin', '*');

signals that our function will accept CORS requests from any other domain. Before a cross-origin request, browsers will send an OPTIONS pre-flight request to confirm that the server will accept it. The response to this request has to indicate which headers can be modified in the subsequent request. In our case, we are going to POST a JSON formatted request so we have to allow the Content-Type header to be modified to permit that:

1
res.set('Access-Control-Allow-Headers', 'Content-Type');

Then we handle the expected POST /books request. Any other request is flagged as unsupported.

Because the web preview domain is authenticated, it is not possible to test the function with an actual frontend running in the browser. The best we can do is use curl to generate an appropriately structured request. Restart the backend and then try the following in another terminal tab (you will have to remove the spaces around “localhost”):

1
curl -H "Content-Type: application/json" -X POST -d '{"isbn":4444,"title":"Smarter Than You Think","pages":321}' http:// localhost :8080/books

Verify that the output contains the information about the new book and check that the document is acutally present in the database.

Stop the backend. Deploy the backend to Google Cloud Functions as follows:

1
gcloud functions deploy feathers --allow-unauthenticated --trigger-http --runtime nodejs12 --set-env-vars MONGODBURI=$MONGODBURI

It will take a few mintues to deploy. When successful, you will see the HTTP endpoint for the function which will look something like:

1
https://your-subdomain-here.cloudfunctions.net/feathers

Test the deployed function as follows:

1
curl -H "Content-Type: application/json" -X POST -d '{"isbn":8888,"title":"Capital","pages":1001}' https://your-subdomain-here.cloudfunctions.net/feathers/books

Set Up the frontend

In another tab, go to the gcpfunc2/frontend directory. Run yarn install.

Update frontend/src/feathers.js with the base URL of your Google Cloud Function (something like https://your-subdomain-here.cloudfunctions.net/feathers).

In frontend/src/Books.js, add the code to import the Feathers client:

import client from './feathers';

In the constructor, initialize the component’s state to empty:

  constructor(props) {
    super(props);

    this.state = {};
  }

Add the code so that when the component mounts, we update the state with a handle to the backend “books” service:

  componentDidMount() {
    const books = client.service('books');

    this.setState({books})
  }

Finally, add the code to the addBook event handler to actually add the book when the button is clicked:

1
2
3
4
5
6
7
8
9
10
this.state.books.create({
    isbn,
    title,
    pages
})
.then(() => {
    inputISBN.value = '';
    inputTitle.value = '';
    inputPages.value = '';
});

Start the development server for the React frontend (PORT=8080 yarn start). Preview and test the application. Make sure any added books show up in the MongdoDB as expected.

Assignment

  1. Modify the frontend and the Google Cloud Function (GCF) so it displays all the books in the database in the table at the bottom of the page. You will have to add the code to the GCF that will handle a GET /books request.
  2. Modify the frontend and the GCF so that books can be deleted. You will have to add the coded to GCF that will handle a DELETE /books/:id request.
  3. Implement CI/CD for the project so that the backend gets deployed to Google Cloud Functions and a working public frontend gets deployed to Firebase.