Builder Book

  1. Introduction
  2. App structure. Next.js. HOC. Material-UI. Server-side rendering. Styles.
  3. Server. Database. Session. Header and MenuDrop components.
  4. Authentication HOC. Promise. Async/await. Static method for User model. Google OAuth.
  5. Testing with Jest. Debugging with Winston. Transactional emails. In-app notifications.
  6. Book and Chapter models. Internal API. Render chapter.
  7. Github integration. Admin dashboard. Testing Admin UX and Github integration.
  8. Table of Contents. Highlight for section. Hide Header. Mobile browser.
  9. BuyButton component. Buy book logic. ReadChapter page. Checkout flow. MyBooks page. Mailchimp API. Deploy app.

Chapter 1: App structure. Next.js. HOC. Material-UI. Server-side rendering. Styles.

We keep the book up-to-date with the latest frameworks and packages.


In Chapter 1, you'll start with the codebase in the 1-start folder of our builderbook repo and end up with the codebase in the 1-end folder. We'll cover the following topics in this chapter:

  • Setup
    - Node and Yarn
    - package.json

  • Code editor and lint
    - VS editor
    - Eslint
    - Prettier

  • App structure
    - Next.js
    - Document
    - Project structure

  • Index page

  • Header component

  • withLayout HOC

  • Material-UI integration
    - Add styles on server
    - Remove server-side styles

  • Server-side rendering


You've read about the motivation for writing this book and building a web application. Motivation aside, this book will teach you how to build a modern-stack, production-ready web application from scratch. Together, we will go from 0 to over 10,000 lines of code in 9 chapters of this book.

In this very first chapter, we have multiple goals:

  • set up our coding environment
  • create our app structure
  • get familiar with Next.js (Next)
  • create our first page and component
  • create a higher-order component
  • integrate Next with Material-UI
  • learn about server-side rendering
  • add global and shared styles

link Setup

We work on Ubuntu 16.04 LTS. We skipped the 17.04 release and decided to wait for stable 18.04. Thus, we provide installation instructions specific to a Linux-based OS (for example, Ubuntu and MacOS). we use the Visual Studio editor (VS editor), which we find easier to use and automate than any other popular editor. The web application that we build in this book will allow you, via integration with Github, to use VS editor for writing documentation and books.

The core technologies of the Builder Book app are React, Material-UI, Next, Express, Mongoose, and MongoDB. By the end of this chapter, we will create a static web app (no server and no database yet). To do so, we will install and integrate the first three technologies - React, Material-UI, and Next.

link Node and Yarn

We are building a Node app, and many of the tools that we use in this app also require Node.

I suggest using Node with the help of nvm (Node Version Manager). On Linux, press Ctrl+Alt+T to open your terminal (alternatively, use the search bar to search for terminal).

  • Run the command below to install nvm:
    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

  • Check the nvm version to confirm successful installation:
    nvm --version

  • Trigger nvm:
    . ~/.nvm/nvm.sh

  • Install Node 10.5.0:
    nvm install 10.5.0

  • Make it default:
    nvm alias default 10.5.0

  • Check Node version to confirm successful installation:
    node -v

Node version should be 10.5.0.

Once Node is installed, we can install Yarn, a manager for third-party packages (also called dependencies or modules). Whenever we need to use code developed by other developers - we add the package name and version to a package.json file and run yarn in the app's directory. More on package.json in the next section.

If you're using Ubuntu:

  • Configure the Debian package repository for Yarn by running the following two commands in your terminal:
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
  • Then install Yarn with:
    sudo apt-get update && sudo apt-get install yarn
  • Check Yarn version to confirm successful installation:
    yarn -v

If you're using another operating system, find specific instructions here on yarn's official website. Select your OS from the "Operating system" dropdown menu.

In this book, the Yarn version is 1.7.1.

link package.json

package.json is a required file at the root of any Node application. This file contains the app's metadata: app name, app version, scripts, and dependenices (described by name and version), among other properties. Read about working with a package.json file to learn more about metadata. Some parameters are optional (keywords, license), but some are required (name, version).

Open the 1-start folder located at builderbook/book/. Take a look at our package.json file:
package.json :

{
    "name": "1-start",
    "version": "0.0.1",
    "license": "MIT",
    "scripts": {
        "build": "next build",
        "start": "next start",
        "dev": "next"
    },
    "dependencies": {
        "@material-ui/core": "^3.3.1",
        "next": "^7.0.2",
        "prop-types": "^15.6.2",
        "react": "^16.6.0",
        "react-dom": "^16.6.0",
        "react-jss": "^8.6.1"
    },
    "devDependencies": {}
}

You can see required metadata like name and version (version format is major.minor.patch).

The section of package.json called scripts contains shortcuts for commands. At the end of this book, the scripts section will contain the following command:

"dev": "nodemon server/app.js --watch server --exec babel-node --presets=@babel/preset-env"

This command (shortcut) allows us to run our app locally by typing yarn dev in the terminal instead of manually typing:

yarn nodemon server/app.js --watch server --exec babel-node --presets=@babel/preset-env

It's ok if you don't understand the above command. We will discuss it detail in Chapter 2 when we write our first server-side code. In this chapter and part of Chapter 2, since there is no server to start, you will run your app locally with yarn next or yarn dev ("dev": "next").

Also in Chapter 2, we'll introduce the dotenv package, which will manage most of our environmental variables (over a dozen in total) in our app.

Inside the scripts commands, you may choose to pass environmental variables. You would set it up by adding NODE_ENV=production to one of the commands in the scripts section. Let's consider an example. At the end of this book, we will start our app locally using yarn dev, which has the following shortcut in the scripts section:

"dev": "nodemon server/app.js --watch server --exec babel-node --presets=@babel/preset-env"

We don't pass any environmental variables inside this command. Thus, NODE_ENV and ROOT_URL default to development and http://localhost:8000, respectively.

We are able to pass environmental variables by prepending them to the command. For example, to build our app on a remote production server, we may use yarn build with this shortcut:

"build": "NODE_ENV=production next build"

In this case, the value of NODE_ENV is explicitly set to production. Next.js will build the production app properly with NODE_ENV equal to production. In Chapter 2, we will discuss how to manage server-side environmental variables. In Chapter 8, we will discuss how to manage universally available (available on client and server) environmental variables.

The next section in our package.json file is dependencies. This section contains a list of third-party packages that we need in production and development environments. To install all packages from package.json, simply run yarn in your terminal while inside the app's directory.

To check if Yarn successfully installed the packages, look for a newly generated node_modules folder and yarn.lock lockfile at the app's root directory. The former folder contains the code of third-party packages, and the latter file contains the exact versions of packages and their dependencies.

In the scripts section, you can specify a combination of commands or run the scripts directly from your files. From our package.json file, if you run yarn dev in your terminal, you will first run yarn next.

devDependencies are dependencies that our app uses in development but not in production. Typically, developers use packages in devDependencies to run tests, compile code, or lint code locally.

If you ran the yarn command inside the 1-start folder, then you successfully installed all packages we need for Chapter 1. We will discuss each installed package later in this chapter.

link Code editor and lint

In this section, we will set up preferences for Visual Studio Code editor (VS editor) and discuss/install two extensions that help us format code: Eslint and Prettier.

link VS editor

I prefer the Visual Studio code editor, because it easily integrates with Github and Eslint (which formats code), and it comes with Terminal (this allows you to stay within the editor while running commands). Here is a typical view of the editor with terminal. A list of staged changes is one click away, and you can see that we have 2 staged changes:
Builder Book

In addition, VS editor has many optional settings that may increase your productivity. VS editor has 2 types of settings: User settings and Workspace settings. Read more in Visual Studio's docs.

The User settings are specific to your machine and will apply globally to any project you work on unless you configure Workspace settings. Since we only want to create settings for our builder book project, and not affect any settings you'd like on your own machine, we will modify the Workspace settings.

You can access Workspace Settings by going to File(or Code)>Preferences>Settings>Workspace Settings. Then click ... at the top right and select Open settings.json. Here is a list of our Workspace Settings:

{
    "window.zoomLevel": -1,
    "files.autoSave": "afterDelay",
    "git.enableSmartCommit": true,
    "editor.formatOnSave": true,
    "eslint.autoFixOnSave": true,
    "prettier.eslintIntegration": true,
}

And here is a snapshot of our User Settings:
Builder Book

  • "window.zoomLevel": -1 sets the zoom level to 80% - our personal preference, since we like to scroll less, especially when writing a tutorial or book.
  • "files.autoSave": "afterDelay" saves files automatically for you, so you don't need to save your files manually.
  • "git.enableSmartCommit": true stages changes automatically, so you don't need to stage your changes manually.
  • "editor.formatOnSave": true detects formatters, such as Eslint, and formats your code on each save event. To format code on a save event, you have to manually save the file.

Below, we will discuss the remaining 2 settings that are related to Eslint and Prettier code formatters.

To format code, we use two extensions in Visual Studio: ESLint by Dirk Baeumer and Prettier-JavaScript formatter by Esben Petersen:
Builder Book

link Eslint

Eslint lints code, i.e. it checks code for potential formatting problems and fixes them. After you install the Eslint extension on VS editor, you will see new settings. Read the full list of settings here. One setting that we want to set to true is:

"eslint.autoFixOnSave": true

This setting makes Eslint apply fixes to your code when you save a file manually. Before we jump in and test Eslint out, we need to create a configuration file and install missing dependencies.

Eslint requires a .eslintrc.js file that contains a list of formatting rules. Formatting rules can specify the type of quotes and commas to use, the maximum length for a line of code, and more. Make sure to place the .eslintrc.js file at the app's root directory:
.eslintrc.js :

module.exports = {
    parser: 'babel-eslint',
    extends: 'airbnb',
    env: {
        browser: true,
        jest: true,
    },
    plugins: ['react', 'jsx-a11y', 'import'],
    rules: {
        'max-len': ['error', 100],
        'no-underscore-dangle': ['error', { allow: ['_id'] }],
        'prefer-destructuring': [
            'error',
            {
                VariableDeclarator: {
                    array: false,
                    object: true,
                },
                AssignmentExpression: {
                    array: true,
                    object: false,
                },
            },
            {
                enforceForRenamedProperties: false,
            },
        ],
        'import/prefer-default-export': 'off',
        'jsx-a11y/anchor-is-valid': 'off',
        'react/react-in-jsx-scope': 'off',
        'react/jsx-filename-extension': [
            'error',
            {
                extensions: ['.js'],
            },
        ],
    },
};

I included the .eslintrc.js file with all necessary rules in our 1-end folder.

To make Eslint work properly, we need to install the missing packages that Eslint relies on.

Add the following dependencies to your package.json file and run yarn:

"devDependencies": {
    "babel-eslint": "^8.2.5",
    "eslint": "^5.0.1",
    "eslint-config-airbnb": "^17.0.0",
    "eslint-plugin-import": "^2.13.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-react": "^7.10.0"
}

A list of all Eslint rules is in the official docs. Check out, for example, the max-len rule.

Here's an example of code with an Eslint error for max-len:

var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" }, "difficult": "to read" };

And here's the same code without the max-len error:

var foo = {
    "bar": "This is a bar.",
    "baz": { "qux": "This is a qux" },
    "easier": "to read"
};

You may have noticed that we installed the eslint-config-airbnb package and extended Eslint with extends: 'airbnb'. In addition to rules that we specified in .eslintrc.js, we use rules from the eslint-config-airbnb package.

Now that you've properly installed and configured Eslint, let's test it.

Create a pages/index.js file inside your 1-start folder, then paste the following code:
pages/index.js :

[1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;
});

Eslint will highlight this code, and when you hover over the highlighted code, you will see a description of the error(s):

[eslint] Unexpected unnamed function. (func-names)
[eslint] Unexpected function expression. (prefer-arrow-callback)

This type of Eslint error is a result of Airbnb's format rule for arrow functions. To fix this error, simply save the file manually with Ctrl+S, and the code will be edited into:
pages/index.js :

[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

To continue reading, buy this book.

We keep the book up-to-date with the latest frameworks and packages.


format_list_bulleted
help_outline