Codementor Events

A Simple MERN Stack Template, pt.1

Published Aug 08, 2018Last updated Feb 04, 2019

I've always dabbled in web development since the early days of Geocities. It wasn't until recently where I decided to take it a bit more seriously, delve into the current technologies and perhaps, even, join it professionally.

To my surprise, the Internet kicked it up into high gear and now there's a language for everything. Alright, I thought to myself, lets buckle up, set up a workspace, and make yourself a playground to learn the popular React with Full-Stack practices.

What I needed to do first was build a bare-bones template with MERN. I exhausted guide after guide that sought to have you build a specific app with little to no explanation. I wanted something I could copy over and over to suit every and any need I would have in the future.

MERN Stack Basics

MERN stands for MongoDB, Express.JS, React.js, and Node.js.

It is a combination of front and back-end JavaScript libraries. MongoDB is your noSQL database. Express is the framework used to build a server on the runtime environment Node.js, and React is the JavaScript library for your front-end pages.

To compile them all together, Webpack and Babel were used.

What We're Gonna Do Here

Before we get to the M and R, we gotta make sure E and N give us a comfortable workspace. Our goal in this article will be to create a nodemon and webpack dev server that will live-reload as we work. We will also bundle, compress and compile our work to keep the app lightweight, and finally hook up Express.JS to serve us an ugly REACT app so we can play around with it.

Here's my Repo of it.
CD into wherever you download it and run 'npm install'

Setting up the Environment

First thing's first. Pretending I had a completely blank slate on my Linux, I needed the following:

Project Folder: Create the folder of your project and change your working directory to it in your terminal.

npm: The package manager for Node.JS | Installation

npm init

Here you initialize your package.json that contains all the rules, dependencies and info for your app. YOu can press enter through all the questions for now.

webpack: The bundler for my client-side code so its not sluggishly gigantic.

npm install --save-dev webpack webpack-dev-server
npm install webpack-cli -D

Note: --save-dev installs it as a developer dependency for your application.

babel: The compiler for all your JS, ES needs.

npm install --save-dev babel-loader babel-core babel-preset-react babel-preset-env babel-preset-stage-1 babel-plugin-transform-runtime

npm install --save babel-runtime

react: Your front-end JavaScript

npm install --save react react-dom

Note: --save installs it as a dependency for your application

ESLint: (Optional) A pluggable linter for js/jsx. Install it then configure it.

npm install eslint --save-dev

nodemon: Monitors changes in files to auto-update your live server.

npm install nodemon

Configuration Files

Create and configure the following files in your root directory if you haven't already. Yes, these files start with a period (.).

package.json

{
  "name": "template",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
      "dev": "webpack-dev-server --env.dev",
      "build": "webpack",
      "start": "nodemon --ignore dist src/server/server.js"
    },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-1": "^6.24.1",
    "eslint": "^5.3.0",
    "eslint-plugin-react": "^7.10.0",
    "webpack": "^4.16.5",
    "webpack-dev-server": "^3.1.5"
  },
  "dependencies": {
    "babel-runtime": "^6.26.0",
    "react": "^16.4.2",
    "react-dom": "^16.4.2"
  }
}

.babelrc

{
    "presets": [
      ["env", {"modules": false}],
      "stage-1",
      "react"
    ],
    "plugins": [
      "transform-runtime"
    ]
  }

.eslintrc.json

{
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "plugin:react/recommended",
    "parserOptions": {
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        },
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ]
    }
}

webpack.config.js

const path = require('path')
const webpack = require('webpack')

module.exports = {
  mode: 'none',
  entry: './src/client',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'http://localhost:8080/dist'
  },
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css']
  },
  devServer: {
    port: 8080
  }
}

Woah, woah, woah. What's all this port:8080, entry, and dist stuff?

Well, I'm getting ahead of myself. You see, what we did was set up a development server using webpack. At the end of this template you'll have your Express server running on port 3000 AND your web dev server running on the other. It's a great way to work live, troubleshoot, and just mess around knowing exactly what you wind up breaking after it reloads.

As for entry/output/dist stuff? That's what you're bundling, where you're bundling to, and where you can find it. Webpack is going to bundle all your separate JS files in your client folder and spit out a lightweight script you'll link in your main HTML page.

Structuring your Root Directory

You're going to need folders and a lot of them. You're organizing a full stack app after all. I try to keep mine as simple as possible.
$ ./template

.
├── node_modules
├── public
│   └── index.html
├── src
│   ├── client
│   │   ├── components
│   │   │   └── App.jsx
│   │   ├── css
│   │   └── index.js
│   ├── models
│   └── server
│       └── server.js
├── .babelrc
├── .eslintrc.json
├── package.json
├── package-lock.json
└── webpack.config.js

Folders you've created and why:
      public: self-explanatory, what users will be allowed access to only.
      src: Your source JS and other goodies for your site.
      client: Everything concerning the app client.
      components: Your React JS files that will serve as building blocks.
      css: Pretty the former up.
      models: This is to store database JS.
      server: Express Server JS.

Files you've created and why:
      index.html: Your home page.
      App.jsx: The first component to your app.
      index.js: The bridge between the html and jsx
      server.js: The express server

Setting up Express

Now, let's install Express as a dependency as well as Compression. It compresses Express. More 'Gotta Go Fast' stuff.

npm install --save express
npm install --save compression

Let's prepare our 'index.html' first.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>My Template</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <script src="http://localhost:8080/dist/bundle.js"></script>
  </body>
</html>

The basics. Note the script addition and its link. That'll change when we host this but for now that'll do.

Let's look at what our 'server.js' file should read:

const compression = require('compression')
const express = require('express')
const path = require('path')

const app = express()
// Gives constant name to long directory home page.
const appPage = path.join(__dirname, '../../public/index.html')

// Compresses App
app.use(compression())
app.disable('etag')
// Allows the use of files.
app.use(express.static(__dirname + './../../'))

// SERVES STATIC HOMEPAGE at '/' URL
app.get('/', function(req, res) {
  res.sendFile(appPage)
})

// Port Setting
app.listen(3000, () => console.log('Running on Port 3000... Do not forget to run "npm run dev" in another terminal!'))

I added some comments to help, but its always best to read the documentation

Nitty Gritty REACT Set Up

I'm not gonna expand on React right now. Besides, if you're looking at a MERN template guide, I'm assuming you're versed in it. I'll provide my ugly ones here just so you can ERN at the very least.

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App.jsx'

ReactDOM.render(<App />, document.getElementById('root'))

App.jsx

import React, {Component} from 'react'

class App extends Component {
    render(){
        return(
            <div>
                {'You are now running React on Express'}
            </div>
        )
    }
}

export default App

Try It Out.

Open up two terminals, one for nodemon and the other for webpack dev.

Terminal 1.

npm start

Terminal 2.

npm run dev

Head on over to http://localhost:3000/ to view your ERN Site!

Final Thoughts and Further Reading

That ought to be enough of a playground to start with Express and React. I've built so many MERN projects by throwing myself around in the dark, so needless to say, they haven't been the prettiest.

On the next installment, I'm hoping to make a more attractive React app and connect MongoDB using Mongoose and mLab

But if you can't wait and wanna keep going:
React
Express
Mongoose
mLab
Webpack
Speaking of webpack, you're gonna need loaders if you wanna add file extensions beyond js and jsx. Read about that here.

Some more libraries I like:
react-router-dom
react-bootstrap

If you're still here, thank you. It's my first write-up ever so I hope you've enjoyed it.

Discover and read more posts from Alejandra Saldaña
get started
post comments3Replies
John Hoey
6 years ago

thank you!

Alimi Kehinde Morufudeen
6 years ago

Awesome

Peterfig
6 years ago

Thanks for the great writeup!