Deploy the application to Firebase

We will now deploy our weather app to the cloud. However there are 2 parts to the app. The react client and your API server.

The react client will be deployed to Firebase Hosting as a static site. A static site is simply a location to store the html, javascript, and css that make up our react application.

The weather API that we created will be deployed to a Serverless Function. We will only deploy the route that fetches the weather for the city and returns daily weather data as JSON.

1 Firebase Project

1.1 Create a new Firebase Project

  1. https://console.firebase.google.com
  2. Sign in to your university google account
  3. “Create a project”
  4. Give your project a unique name and select the university parent organization:

    new project

  5. Disable google analytics:

    disable analytics

1.2 Add Firebase into your project

  1. Start in the root of your weather-app project (the same directory as your package.json file).

  2. Build your react project:
    npm run build
    

    Note: you now have a build directory containing the files required to deploy your application.

  3. Add the firebase-tools package
    npm install --save-dev firebase-tools
    
  4. Add a npm run-script to make it easier to run.

    package.json:

    1
    2
    3
    4
    5
    6
    7
    8
    
    // ...
    
    "scripts": {
      // ...
      "firebase": "firebase"
    },
    
    // ...
    
  5. Login to Firebase:
    npm run firebase -- --help
    npm run firebase -- login
    

2 Hosting your react site

  1. Enable Firebase hosting:
    npm run firebase -- init
    
    1. Enable Hosting with the space bar and then press enter.
    2. “Select a default Firebase project for this directory” select don't setup a default project and press enter
    3. ⚠️ ☣️ DO NOT SKIP THIS STEP ☣️ ⚠️️ “What do you want to use as your public directory? (public)”: enter dist, the directory containing your built react application.
    4. “Configure as a single-page app (rewrite all urls to /index.html)?”: enter y because our react application is a single page application.
    5. “Set up automatic builds and deploys with GitHub?” enter n.
    6. “File build/index.html already exists. Overwrite?”: enter n since we want to use the index.html generated by the react build process.

    💡 Note that Firebase added 2 new files, .firebaserc that remembers which firebase project to use by default and firebase.json that contains some firebase settings, including those that you configured above.

  2. Link your local project to your Firebase project:
    npm run firebase -- use --add
    
    1. ” Which project do you want to add?” select the project you create before and press enter
    2. “What alias do you want to use for this project? (e.g. staging)” enter default and press enter
  3. Deploy your application!
    npm run firebase -- deploy
    
  4. Navigate to the URL provided by firebase in console. It should resemble: https://{your project name}.web.app

    Try accessing your site with smartphone. You just deployed your first site, with SSL, to the cloud… for free!

  5. In your browser, in the Firebase console, go to the “Hosting” page. Here you could associate a custom domain. You can also see your deployment history with the option to rollback to a previous deployment.

Exercise 2.1 Update your site and redeploy. Use the Firebase console to roll back your deployment.

3 Add serverless functions to your project

  1. Enable Firebase functions:
    npm run firebase -- init
    
    1. Choose “Functions”
    2. Select “JavaScript”
    3. “Do you want to use ESLint to catch probable bugs and enforce style?” enter n
    4. “Do you want to install dependencies with npm now?” enter n
  2. Modify your functions to use the node.js version 18 runtime.

    In functions/package.json modify the engines entry:

    1
    2
    3
    
    "engines": {
      "node": "18"
    },
    
  3. Install the dependencies with npm:
    cd functions
    npm install
    
  4. In vscode, open functions/index.js and un-comment all of the lines of code. Then rename the function to weather:

    1
    2
    3
    4
    
    exports.weather = functions.https.onRequest((request, response) => {
    functions.logger.info("Hello logs!", {structuredData: true});
    response.send("Hello from Firebase!");
    });
    
  5. Open the /firebase.json file. Add a rewrite rule so that you function is accessible in the same domain as your hosting. Your rewrite rules should resemble the following:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    "rewrites": [
      {
        "source": "/api/weather",
        "function": "weather"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
    
  6. Re-deploy your project ⚠️ from the root of your project ⚠️:
    cd ..
    npm run firebase -- deploy
    
  7. ⚠️ You will get an error message the first time you deploy your function:

    Error: Your project weather-cpinfo20 must be on the Blaze (pay-as-you-go) plan to complete this command.

    Follow the link and add purchase the firebase “Blaze” plan using the billing account created with the free credits that you received from Google Cloud.

    blaze plan

    After upgrading the project, re-execute npm run firebase -- deploy.

  8. You just deployed your first serverless cloud function! You can access either directly in the URL listed in the console:

    https://us-central1-{your project name}.cloudfunctions.net/weather

    Or at the path that we configured in the hosting (notice the path is api):

    https://{your project name}.web.app/api/weather

    ☝️ Take a moment to appreciate what we now have. A website deployed, with javascript backend configured to exact routes. Plus, we never configured a server, virtual machine, docker, or paid for hosting!

💡 The function onRequest() works the same way as a route in express with the request and response parameters.

Exercise 3.1 Copy your getWeather() function from your express app into functions/index.js and call it from the weather function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const functions = require('firebase-functions');

exports.weather = functions.https.onRequest(async (request, response) => {
  const city = request.query.city;
  functions.logger.info("Getting weather for city", {city});
  const weather = await getWeather(city);
  functions.logger.info("Fetch weather data", {weather});
  response.send(weather);
});

async function getWeather(city) {
  // Your getWeather() function...
  
  // 1. get long/lat for city
  // 2. get weather for long/lat
  // ...
}

Bonus: Use firestore in your app to track recent cities and or provide accounts to remember the previous city.

Bonus: Use next.js, gatsby, or react-router to create multiple pages in your app