Home Static Sites
Static Sites
Cancel

Static Sites

Learning Outcomes

  • assess whether a website is better suited as a dynamic or static site
  • use a static site generator to create a static website
  • deploy a static static site to a hosting service tuned for serving static sites

Resources

Lab

Video walkthrough of this lab.

Review Dynamic Page Generation with Express

Fork this project in GitLab: https://gitlab.com/langarabrian/courses2.

After forking, go to “Settings | General | Advanced” and remove the fork relationship.

Go to “Settings | General | Visibility …” and make the project visibility private and save the changes.

Start up your Google Cloud Shell.

Clone the project using “git clone {your project SSH URL}”.

In MongoDB Atlas, create a “courses” collection where documents have the following shape:

{
    subject: "CPSC",
    course: 1030,
    title: "Web Development I",
    credits: 3,
    description: "Students will examine the structure of the Internet and the World Wide..."
}

Then you should be able to install the dependencies and run the application as follows:

1
2
3
4
npm install
export MONGODBURI='mongodb+srv://<USERNAME>:<PASSWORD>@<HOSTNAME>'
export MONGODBNAME='<DBNAME>'
PORT=NNNN DEBUG=courses2:* npm run dev

Make sure that you understand how this application works before continuing to the next step.

Replacing Dynamic Page Generation with Static Site Generation using Gatsby

In this section, we are going to re-implement the course catalogue appplication above using the Gatsby static site generator.

Instal the Gatsby CLI:

1
npm i -g gatsby-cli

Fork this project in GitLab: https://gitlab.com/langarabrian/gatsby-courses2.

After forking, go to “Settings | General | Advanced” and remove the fork relationship.

Go to “Settings | General | Visibility …” and make the project visibility private and save the changes.

Clone the project using “git clone {your project SSH URL}”.

Make sure the project runs as expected:

1
2
3
cd gatsby-courses2
npm install
PORT=8080 gatsby develop

You should be able to preview the running application. The index page is supposed to display the list of courses in the program. Right now there is just an empty table. We are going to pull the data from our MongoDB Atlas database. The first step is is to install and enable the MongoDB plugin for Gatsby:

1
npm i -s gatsby-source-mongodb

then add the following in the plugins array in gatsby-config.js:

1
2
3
4
5
6
7
8
9
10
11
12
{
    resolve: `gatsby-source-mongodb`,
    options: {
        connectionString: process.env.MONGODBURI,
        dbName: process.env.MONGODBNAME,
        collection: `courses`,
        extraParams: {
            retryWrites: true,
            w: "majority",
        },
    },
},

Restart the development server and verify the site still works:

1
PORT=8080 gatsby develop

Use the GraphiQL browser to browse the data for the site. You get access to the GraphiQL browser by putting /___graphql after the hostname in the browser address bar. There should be tree browser in the left-hand side and you should be able to open up “allMongodbCpsc2650Courses | edges | node” and then check “subject”, “course”, and “title”. You should see the GraphQL query previewed in the middle column. You can run the query and see the results by clicking the “play” button in the tool bar. You should see the contents of the collection in the far right column.

NOTE: “allMongodbCpsc2650Courses” may be different for you, but it should be of the form “allMongodbDddddCcccc” where “Ddddd” is the capitalized name of your database and “Ccccc” is the capitalized name of your collection.

Copy the GraphQL query from the middle column to your clipboard.

We are going to modify the index page to display the list of courses. The first step is to bring in the graphql module at the top of src/pages/index.js:

1
import { graphql } from "gatsby";

Then, place the query to get all the courses at the bottom of src/pages/index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
export const query = graphql`
query MyQuery {
  allMongodbCpsc2650Courses {
    edges {
      node {
        subject
        course
        title
      }
    }
  }
}
`

(Remember, you may have to adjust the above depending on your database and collection names.)

Then inject the data resulting from the query into the page rendering function. Just change the function definiton so that it accepts the { data } argument:

1
export default function Home({ data })

Finally, we can fill in the <tbody> element with the code to iterate over all the courses:

1
2
3
4
5
6
7
            {data.allMongodbCpsc2650Courses.edges.map(({ node }, index) => (
                <tr key={index}>
                    <td>{node.subject}</td>
                    <td>{node.course}</td>
                    <td><a href={'/course/' + node.subject + '/' + node.course}>{node.title}</a></td>
                </tr>
            ))}

Test the index page and ensure that it displays the list of courses:

Now we need to programmatically generate the course detail pages. This involves 3 steps:

  1. creating a template for the course detail page
  2. decorating each data node that is going to become a course detail page with a URL slug
  3. generating the final pages

Create the course detail template page, src/templates/course.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
23
24
25
26
27
28
29
30
31
32
import React from "react";

export default function Course() {
  return (
    <div>
      <h1>SSSS NNNN blah blah bah</h1>
      <table>
        <tr>
            <th>Subject</th>
            <td>SSSS</td>
        </tr>
        <tr>
            <th>Course</th>
            <td>NNNN</td>
        </tr>
        <tr>
            <th>Title</th>
            <td>blah blah blah</td>
        </tr>
        <tr>
            <th>Credits</th>
            <td>N</td>
        </tr>
        <tr>
            <th>Description</th>
            <td>description goes here</td>
        </tr>
    </table>
    </div>
  );
}

Decorate each node that is going to become a course detail page by creating a file, gatsby-node.js as follows:

1
2
3
exports.onCreateNode = ({ node, actions }) => {
  console.log( node.internal.type);
}

This code doesn’t actually decorate the desired nodes, it just logs type of every node as they are created. Restart the development server and watch the extra output in the console.

We aren’t interested in all the nodes, just the course nodes, so modify the code as follows:

1
2
3
4
5
exports.onCreateNode = ({ node, actions }) => {
  if (node.internal.type === `mongodbCpsc2650Courses`) {
    console.log( node.internal.type);
  }
}

and restart the development server. Now that we have targetted the right nodes, we can finally decorate them with the desired URL slug which is going to based on the subject code and course number (e.g. /course/CPSC/1030/). Make these final changes and restart the development server:

1
2
3
4
5
6
7
8
9
10
exports.onCreateNode = ({ node, actions }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `mongodbCpsc2650Courses`) {
    createNodeField({
      node,
      name: `slug`,
      value: `/course/${node.subject}/${node.course}/`,
    });
  }
}

You can check that the slugs have been set correctly using GraphiQL.

Now that the nodes have the slug data, we need to generate the actual pages. At the top of the gatsby-node.js file, import the path module:

1
const path = require(`path`);

Then, at the bottom of the file, define the createPages function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
exports.createPages = async ({ graphql, actions }) => {
  // **Note:** The graphql function call returns a Promise
  // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise for more info
  const { createPage } = actions;
  const result = await graphql(`
    query {
      allMongodbCpsc2650Courses {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `);

  result.data.allMongodbCpsc2650Courses.edges.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: path.resolve(`./src/templates/course.js`),
      context: {
        // Data passed to context is available
        // in page queries as GraphQL variables.
        slug: node.fields.slug,
      },
    })
  });
}

The first part of the function queries for all the slugs. The second part iterates over all the slugs and generates a page for each one using the template. If you make this change and restart the development server, you should see that all the links on the index page work now except that all the pages display the same static text.

The final step is update the template to display the actual course data. At the top of src/templates/course.js, import the graphql module:

1
import { graphql } from "gatsby";

At the bottom of the file, add a query to get the data for the specific course:

1
2
3
4
5
6
7
8
9
10
11
export const query = graphql`
  query($slug: String!) {
    mongodbCpsc2650Courses(fields: { slug: { eq: $slug } }) {
      subject
      course
      title
      credits
      description
    }
  }
`

Notice the use of the $slug variable. This was sent from our call to createPage in gatsby-node.js. Finally, update the render function to use the data values resulting from the query:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
export default function Course({ data }) {
  const course = data.mongodbCpsc2650Courses;
  return (
    <div>
      <h1>{course.subject} {course.course} {course.title}</h1>
      <table>
        <tr>
            <th>Subject</th>
            <td>{course.subject}</td>
        </tr>
        <tr>
            <th>Course</th>
            <td>{course.course}</td>
        </tr>
        <tr>
            <th>Title</th>
            <td>{course.title}</td>
        </tr>
        <tr>
            <th>Credits</th>
            <td>{course.credits}</td>
        </tr>
        <tr>
            <th>Description</th>
            <td>{course.description}</td>
        </tr>
    </table>
    </div>
  );
}

Verify that everything is working in the browser preview.

Now we can generate the actual static site. Stop the development server and enter the following command:

1
gatsby build

This generates the static site in the public directory. Gatsby includes a static web server that you can use to verify the site is going to work correctly as built:

1
gatsby serve -p 8080

Refresh the web preview and verify that it still works. Look over the public directory and you can see that there are individual .html files for each course.

Deploying a Static Site

There are many options for deploying a static site in production. In this exercise we will use Firebase Hosting. Head over to https://firebase.google.com/ and sign up / sign in with your Google account. Then go to Firebase console and add a new project. DO NOT ENABLE GOOGLE ANALYTICS.

Back in your Google Cloud Shell terminal, install the Firebase CLI tools:

1
npm install -g firebase-tools

Then sign in to Google:

1
firebase login --no-localhost

Because you are using the --no-localhost flag, you will see a URL that will have to copy and paste manually into a new browser tab. You will then be prompted to select a Google account. Then you will see a token that you will have to copy and paste back into the terminal.

Be sure you are at the root directory of your gatsby-courses2 project and intilaize Firebase for the project:

1
firebase init

When you are asked to select features, only choose “Hosting”. Under “Project Setup”, choose “Use existing project” and select the project you created above. When asked about the “public directory” go with the default (public). When asked to configure as a single page app, choose “No”. When asked to overwrite “public/index.html”, choose “No”.

Finally, deploy the site as follows:

1
firebase deploy

When the deploy operation is finished it will give you a URL where you can access the site.

Assignment

  1. Create another MongoDB Atlas collection that has a list of instructors. Include properties such as name, title, phone, and office. You can some sample data from the Computing Science Faculty List.
  2. Add a new page to the gatsby-courses2 project that displays the list of instructors.
  3. Implement CI/CD (.gitlab-ci.yml) for the gatsby-courses2 project. If you are using the stock node:alpine image, you will have install the Gatsby CLI during the build phase and the Firebase CLI during the deploy phase. Also, you will have to arange to have the MONGODBURI and MONGODBNAME environment variables available during the build phase. Also, you will need a FIREBASE_TOKEN to use during the deploy phase; you can generate one using the firebase login:ci --no-localhost command. You will need to use the artifacts:paths parameter in the build job so that the public directory created by the build job is available to the deploy job.