Developing a Serverless Web Application Completely on Google Cloud (1 of 2)

Brian Jung
Google Cloud - Community
11 min readSep 16, 2022

--

Building a serverless web application with Firebase and NodeJS without downloading anything!

Introduction

Purpose

The purpose of this article is to help readers understand the fundamentals of a few Google Cloud products used for application development, and get hands-on experience with building a Firebase web application on Google Cloud Platform without having to download anything to their local environment. By the end of this article, the readers will get a glimpse into building a serverless web application. This article will show how to use a real-time NoSQL database with Cloud Firestore in Native mode, enable authentication with Firebase, store static files with Cloud Storage, perform logic with no infrastructure management using Cloud Functions, and deploy the application with App Engine.

Feel free to use this Github page to follow along. Each branch has sample code you should have by the end of that specific step.

Highlights

  • Using Cloud Shell with an online development environment that has built in Cloud Code plugin support and language support for Go, Java, .Net, Python and NodeJS called Cloud Shell Editor.
  • Using NodeJS, ReactJS and Firebase to create a web application with user authentication, real-time database, storage and cloud functions without downloading anything.
  • Deploy your website with zero server management and zero configuration deployments using App Engine. Then try application versioning and creating development, testing and production environments.

Audience

This article should be relevant to anyone who wants to learn more about application development on Google Cloud Platform. I wrote this with a few prerequisites in mind.

  • Basic Debian-based Linux terminal skills
  • Basic understanding of cloud architecture
  • Some experience with NodeJS and ReactJS
  • Credit Card to set up a Google Cloud Project

Disclaimer

The article is not a recommended practice for building and deploying production ready applications, only a fun way to explore the Google Cloud Platform and learn. Any opinions in this article are my own and not those of Google.

Google Inc makes no guarantee or warranty concerning the accuracy of said information and shall not be responsible for any loss or damage of whatever nature resulting from the use of, or reliance upon it. Google Inc does not guarantee that the use of any information contained herein will not infringe upon patent, trademark, copyright, or rights of third parties.

The entire demo can be done with the free trial credit ($300 in free credits for new customers still available as of September 16th 2022) and most services should not exceed the free quotas. However, it is your responsibility to follow the clean up instructions to stop all services to avoid incurring charges to your account.

Setting Up

Setting up Google Cloud

Google Cloud Platform is a public cloud vendor from Google that offers virtual resources to users around the world. Google Cloud services run on the same infrastructure that Google uses internally for end-user products such as Search and Youtube. This guide will give an example of how to develop and host a web application using Google Cloud. Read more about the overall landscape of Google Cloud.

To get started, let’s sign in to the Cloud Console using a Google Account. If you are using a company account, you must have permission to sign in to the Cloud Console. Feel free to also create a new account to explore Google Cloud. If this is your first time accessing Cloud Console, you will need to accept all the necessary agreements and set up billing with a credit card. (Don’t worry Google will not charge you until after the trial)

Once you are in the Cloud Console, create a Google Cloud Project that acts as the top level container holding all your application resources as well all other Google Cloud resources. Let’s name this new project my-test-project.

A tip for those who are worried about running out of free resources and being charged, feel free to go to the billing page to check on your resources utilization and credit left.

Setting up a development environment

As companies continue migrating their applications and services to the cloud, developers need access to cloud resources. Google offers Cloud Shell, an online development and operations environment accessible anywhere with your browser. Cloud Shell also comes with a Cloud Shell Editor powered by the Eclipse Theia IDE platform. Cloud Shell Editor comes prepackaged with Google Cloud CLI and allows users to have access to cloud native development through rich language support.

To set up our development environment, let’s use Source Repository and Cloud Shell. Source Repository will allow for collaboration and version control. Open up Cloud Shell from your Cloud Console by clicking on Activate Cloud Shell at the top of the Google Cloud console. Wait as Google Cloud provision your Cloud Shell and connect to the environment.

Now in Cloud Shell, create an empty repository called myapp.

gcloud source repos create myapp

If the Source Repository API was not enabled, it will ask you to enable and retry. Once you see that myApp has been created, clone the repository to your current Cloud Shell environment by running the command below after replacing [YOUR PROJECT ID] with your Project ID. You can also access your new repository from the Cloud Source Repositories page and clone to your local environment.

gcloud source repos clone myapp --project=[YOUR PROJECT ID]

Now launch the Cloud Shell Editor by clicking on the Open Editor button on the toolbar of the Cloud Shell window. You will be able to see the empty myApp directory you have created.

Creating a simple ReactJS application with multiple pages

Now for the final step of setting up, let’s create a new React app in your repository. Go back to the Cloud Shell, change directory into the repository and run create react app. You will use npx here, a npm package runner that comes with npm. Notice you did not have to install npm, this is because npm comes preinstalled in your Cloud Shell instance.

npx create-react-app my-app

Head back to the Cloud Shell Editor, and you should see a basic set up for a ReactJS application. Back in Cloud Shell, change directory into my-app, and try running the app.

npm start

There is something already running on port 3000, so press “y” when it asks to run in a different port. To view the running application, press on the web preview button, change preview port to the local port (3001 for me) and preview. You can keep this tab open, and any edits to App.js from the Cloud Shell Editor will update this page.

We want to create an application with multiple pages, and this can be done by using React Router. First let’s strip the App.js file down to the bare minimum. The function App returns the component you will see on the webpage. Change App.js and save it to see the effect on the build.

// App.js
import './App.css';

function App() {
return (
<div className="App">
This is an App
</div>
);
}
export default App;

You should see a rather empty page with the words “This is an App” in the top center of the webpage. Now let’s create different pages for our application. First create a directory inside /src called pages. Inside pages, create three new javascript files and name them HomePg.js, LoginPg.js, and PostPg.js respectively. Copy the contents of App.js excluding the import ‘./App.css’; line to these new pages, and change the function name and export to HomePg, LoginPg, PostPg respectively.

You should have the following scripts including the App.js script given above.

//HomePg.js
import
React from "react";

function HomePg() {
return (
<div className="App">
This is the home page
</div>
);
}
export default HomePg;
//LoginPg.js
import React from "react";

function LoginPg() {
return (
<div className="App">
This is the login page
</div>
);
}
export default LoginPg;
//PostPg.js
import React from "react";

function PostPg() {
return (
<div className="App">
This is the post page
</div>
);
}
export default PostPg;

Now we are going to import these pages to the main application and create routes for users to navigate to these new pages. First, let’s use npm to install the react-router-dom library that will allow us to use React Router in web applications. Go back to Cloud Shell and in my-app directory run the following command. (If you’re still running my-app in the browser you can use control+c to close it.)

npm install react-router-dom

Finally in the main application import the pages and create a navigation bar and routes. It should look like the following snippet.

//App.js
import './App.css';
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";

import HomePg from "./pages/HomePg";
import PostPg from "./pages/PostPg";
import LoginPg from "./pages/LoginPg";

function App() {
return (
<Router>
<nav>
<Link to="/"> Home </Link>
<Link to="/login"> Login </Link>
<Link to="/post"> Post </Link>
</nav>
<Routes>
<Route path="/" element={<HomePg/>} />
<Route path="/post" element={<PostPg/> } />
<Route path="/login" element={<LoginPg/> } />
</Routes>
</Router>
);
}

export default App;

Now when you run the application and view, you should see a way to navigate to the different pages, try playing around with the different pages.

npm start

You have created a simple web application with multiple pages, and a way to navigate through these pages. Feel free to take a break here before moving on to the next part where you will be integrating Firebase and creating an Authentication process for your application. Before you close everything, let’s make sure all the hard work is saved to the Cloud Source Repository. First let’s make sure you have an identity by running the following code in Cloud Shell, change [YOUR EMAIL] and [YOUR NAME] with your email address and name.

git config --global user.email "[YOUR EMAIL]"
git config --global user.name "[YOUR NAME]"

Now add everything you have created, and commit the changes.

>> git add .
>> git commit -m "[COMMIT MESSAGE]"

When you commit your changes, git will abort the commit if the commit message is empty. Please replace the [COMMIT MESSAGE] with any message or first commit . Finally you have to push this to the Cloud Source Repository where your code will be safely stored.

git push

Congratulations, you have successfully pushed your work to the Cloud Source Repository!

Integrating Firebase

Adding Firebase to the project

Firebase is a platform developed by Google for creating mobile and web applications, and it makes developing applications a lot easier. To get started with Firebase, we need to add Firebase to an existing Google Project. Remember when we set up Google Cloud we created our project my-test-project that we’ve been working on. Head to the Firebase console, log in with the Google account you have been using. Once you have logged in, click Add project. Select your existing Google Cloud project from the dropdown menu, then click Continue. For this demo we don’t need to enable Google analytics, finish by clicking on Add Firebase.

You have connected Firebase to the project! However, we still need to add Firebase to our application. The project page in the Firebase console will have a get started button for Web application with a HTML Tag icon. Pressing on it will ask for an app nickname, let’s name it my-first-web-app. Don’t check the box for Firebase Hosting, we will do hosting later. Click Register app.

This page now gives the direction to add Firebase SDK. Head back to Cloud Shell, and in my-app directory, install the Firebase SDK with npm.

npm install firebase

Next, we have to initialize Firebase for our application in our React app. Let’s go back to the Cloud Shell Editor and within the src directory, add a firebase-config.js javascript file. Copy the code snippet from the Firebase console to the firebase-config.js file. It should look something like this. Don’t make any changes to configuration!

// firebase-config.js
// Import the functions you need from the SDKs you need

import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "[YOUR API]",
authDomain: "[YOUR AUTH DOMAIN]",
projectId: "[YOUR PROJECT ID]",
storageBucket: "[YOUR STORAGE BUCKET]",
messagingSenderId: "[YOUR MESSAGING SENDER ID]",
appId: "[YOUR APP ID]"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

You have added Firebase to your application although currently it is not doing anything for your application. We will use Firebase authentication to create an authentication method and keep track of users.

Using Firebase Authentication

To use Firebase authentication, we have to edit the configuration to add the SDKs. We will be using the getAuth and GoogleAuthProvider methods from the firebase/auth library for our authentication logic. Let’s import them and add to our initialized application. Now the firebase-config.js file should look more like this.

// firebase-config.js
// Import the functions you need from the SDKs you need

import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
import { getAuth, GoogleAuthProvider } from 'firebase/auth';
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "[YOUR API]",
authDomain: "[YOUR AUTH DOMAIN]",
projectId: "[YOUR PROJECT ID]",
storageBucket: "[YOUR STORAGE BUCKET]",
messagingSenderId: "[YOUR MESSAGING SENDER ID]",
appId: "[YOUR APP ID]"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Adding Authentication
export const auth = getAuth(app);
export const provider = new GoogleAuthProvider();

Now let’s go to App.js. We need to find a way to know if a user is logged in or not, and we will be using states from react to keep track. State allows us to manage changing data in an application, and will be used later for user input. Let’s import useState from react and create a boolean state authState that tells the application if the user is authenticated or not. This state also needs to be passed to the different pages.

// App.js
import './App.css';
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import { useState } from "react";

import HomePg from "./pages/HomePg";
import PostPg from "./pages/PostPg";
import LoginPg from "./pages/LoginPg";

function App() {
const [authState, setAuthState] = useState(localStorage.getItem("authState"));

return (
<Router>
<nav>
<Link to="/"> Home </Link>
<Link to="/login"> Login </Link>
<Link to="/post"> Post </Link>
</nav>
<Routes>
<Route path="/" element={<HomePg authState={authState}/>} />
<Route path="/post" element={<PostPg authState={authState}/> } />
<Route path="/login" element={<LoginPg setAuthState={setAuthState}/> } />
</Routes>
</Router>
);
}

export default App;

Now let’s create a way to log in. Head to the LoginPg where we will create a way to log in with Google. We want to create a button that will run a function when it is clicked on. First import auth and provider we created from the firebase-config file. Then also import signInWithPopup from firebase/auth library. We will also use the useNavigate function from react-router-dom to navigate back to the home page after signing in. The resulting code should look something like this.

//LoginPg.js
import React from "react";
import { auth, provider } from '../firebase-config';
import { signInWithPopup } from 'firebase/auth';

function LoginPg({setAuthState}) {

const signingIn = () => {
signInWithPopup(auth, provider).then((result) => {
localStorage.setItem("authState", true);
setAuthState(true);
});
};

return (
<div className="Login">
<p>Sign In!</p>
<button onClick={signingIn}> Google Sign In </button>
</div>
);

}
export default LoginPg;

If you run npm start and try to test out the login feature, you will notice that it’s not working! This is because a domain needs to be authorized for OAuth redirects. Head to the Authentication tab in the Firebase console. Go to Settings -> Domains -> Authorized Domains and add domain. Copy the website of the running application (hint. If you’re following the directions it should end with cloudshell.dev) and add the domain. Now it works!

Finally, let’s add logic to make sure that once the user is logged in, they don’t see the login page and instead they see a logout option. Let’s also go ahead and make the post page only visible once the users have logged in.

//App.js
import './App.css';
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import { useState } from "react";

//Firebase imports
import { auth } from './firebase-config'
import { signOut } from "firebase/auth";

//Pages for the application
import HomePg from "./pages/HomePg";
import PostPg from "./pages/PostPg";
import LoginPg from "./pages/LoginPg";

function App() {
const [authState, setAuthState] = useState(localStorage.getItem("authState"));

const signMeOut = () => {
signOut(auth).then(() => {
localStorage.clear();
setAuthState(false);
});
};

return (
<Router>
<nav>
<Link to="/"> Home </Link>
{!authState && ( <Link to="/login"> Login </Link> )}
{authState && (
<>
<Link to="/post"> Post </Link>
<button onClick={signMeOut}> Log Out </button>
</>
)}
</nav>
<Routes>
<Route path="/" element={<HomePg authState={authState}/>} />
<Route path="/post" element={<PostPg authState={authState}/> } />
<Route path="/login" element={<LoginPg setAuthState={setAuthState}/> } />
</Routes>
</Router>
);
}

export default App;

Congratulations! You have created an application with authentication. You can keep track of your users in the Firebase console Authentication Tab. Feel free to push the update to Cloud Repository and take a break here!

Next Steps

In the next part, I will outline how to integrate databases and storage and also deploy the application using App Engine. Finally, I will give an example of Cloud Functions. See you there!

Developing a Serverless Web Application Completely on Google Cloud (2 of 2)

--

--