MERN stack setup

Deepak Kadarivel
5 min readAug 9, 2019

--

Part 1 of Fullstack Javascript application with MERN

Simple MERN (Mongo, Express, React, Node) project setup. An ideal Fullstack Javascript environment to build WEB applications. Including tools configuration for debugging, transpiling, minification, bundling, hot reload ….

This setup assumes Node is already configured in your system.

Github: repo

Step1:

Create a project directory and initialize the project with package.json

mkdir mern-setupcd mern-setupnpm init -y

Step2:

Adding the required packages to the project.

KEY Modules

  1. react
  2. react-dom
  3. express
  4. mongodb

DevDependancies Modules

Babel modules for converting ES6 & JSX to suitable Javascript for all browsers.

  1. babel-core
  2. babel-loader
  3. babel-preset-env
  4. babel-preset-react
  5. babel-preset-stage-2
  6. @hot-loader/react-dom

Webpack modules to bundle compiled Javascript.

  1. webpack
  2. webpack-cli
  3. webpack-node-externals
  4. webpack-dev-middleware
  5. webpack-hot-middleware

Watchers for client and server code

  1. nodemon
  2. react-hot-loader
npm install react react-dom express mongodb react-hot-loader -savenpm install @hot-loader/react-dom babel-core babel-loader babel-preset-env babel-preset-react babel-preset-stage-2 nodemon webpack webpack-cli webpack-dev-middleware webpack-hot-middleware webpack-node-externals --save-dev

Step3:

Configuring Babel

create a .babelrc file in the project folder and add presets and plugins

{
"presets": [
"env",
"stage-2",
"react"
],
plugins: [
"react-hot-loader/babel"
]
}

Step4:

Configuring Webpack

Create 3 Webpack configurations for bundling Server, client-side and client-side production code.

Step4.1:

Client-Side Webpack configuration for development.

create webpack.config.client.js file in the project folder

const path = require('path');const webpack = require('webpack');const CURRENT_WORKING_DIR = process.cwd();const config = {  name: 'browser',  mode: 'development',  devtool: 'eval-source-map',  entry: [    'react-hot-loader/patch',    'webpack-hot-middleware/client?reload=true',    path.join(CURRENT_WORKING_DIR, 'client/index.js'),  ],  output: {    path: path.join(CURRENT_WORKING_DIR, '/dist'),    filename: 'bundle.js',    publicPath: '/dist/',  },  resolve: {    alias: {      'react-dom': '@hot-loader/react-dom',    },  },  module: {    rules: [      {        test: /\.jsx?$/,        exclude: /node_modules/,        use: ['babel-loader'],      },    ],  },  plugins: [    new webpack.HotModuleReplacementPlugin(),    new webpack.NoEmitOnErrorsPlugin(),  ],};module.exports = config;
  • mode sets process.env.NODE_ENV to given value, by default the value is set to producition.
  • devtool specifies how source maps are generated.
  • entry specifies path and filename to start bundeling.
  • output specifies path and filename to generate bundled code.
  • publicPath specifies path to export all assets.
  • module sets regex rule for file extension to be used for transpilation, and folders to be excluded and transpilation tool to be used.
  • HotModuleReplacementPlugin enables hot module replacement for react-hot-loader.
  • NoEmmitOnErrorsPlugin allows skipping emitting when there are compile errors.

Step4.2

Client-Side Webpack configuration for development.

create webpack.config.client.production.js file in the project folder

const path = require('path');const webpack = require('webpack');const CURRENT_WORKING_DIR = process.cwd();
const config = { mode: 'production', entry: [path.join(CURRENT_WORKING_DIR, 'client/index.js')], output: { path: path.join(CURRENT_WORKING_DIR, '/dist'), filename: 'bundle.js', publicPath: '/dist/', }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: ['babel-loader'], }, ], },};module.exports = config;

Step4.3

Server-Side Webpack configuration.

create webpack.config.server.js file in the project folder

const path = require('path');const nodeExternals = require('webpack-node-externals');const CURRENT_WORKING_DIR = process.cwd();const config = {  name: 'server',  entry: [path.join(CURRENT_WORKING_DIR, './server/index.js')],  target: 'node',  output: {    path: path.join(CURRENT_WORKING_DIR, '/dist/'),    filename: 'server.generated.js',    publicPath: '/dist/',    libraryTarget: 'commonjs2',  },  externals: [nodeExternals()],  module: {    rules: [      {        test: /\.js$/,        exclude: /node_modules/,        use: ['babel-loader'],      },    ],  },};module.exports = config;
  • mode is not set here explicitly but will be passed as required when running webpack commands.

Step5

Configuring Nodemon

create nodemon.js file in the project folder

{  "verbose": false,  "watch": [    "./server"  ],  "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"}

This nodemon configuration will watch for changes in server files during development and execute compile and build commands as necessary.

Step6

Linting and Formatting with ESLint and Prettier

ESLint and prettier help maintain code quality.

Step6.1

Add ESLint & prettier dependencies

npm install --save-dev eslint prettier eslint-config-prettier eslint-plugin-prettier

Ste6.2

Add Commonly used Airbnb ESLint rules with dependencies

npx install-peerdeps --dev eslint-config-airbnb

Step6.3

edit .eslintrc.json file in the project folder

{  "env": {    "browser": true,    "node": true  },  "extends": ["airbnb", "prettier"],  "plugins": ["prettier"],  "rules": {    "prettier/prettier": ["error"],    "react/jsx-filename-extension": [      1,      { "extensions": [".js", ".jsx"] }    ]  }}

Step6.4

Add prettier configuration

create .prettierrc file in the project holder

{  "semi": true,  "trailingComma": "all",  "singleQuote": true,  "printWidth": 70}

Step6.5

Add Husky for linting before pre-commit

npm install -D pretty-quick husky

Configure pre-commit hook in package.json

{   
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
}
}

Step7

Template file to serve the React components.

create a template.js file in the project folder.

const template = () => {  return `<!DOCTYPE html>    <html lang="en">    <head>      <meta charset="utf-8">      <title>MERN Setup</title>    </head>    <body>      <div id="root"></div>      <script type="text/javascript" src="/dist/bundle.js"></script>    </body>  </html>`;};module.exports = template;

When the server receives a request to the root URL, the template HTML file will be rendered and div element with id “root” will render the react components.

Step8

Setting up client folder for frontend.

create a subfolder called “client” in the project folder.

create a file called “index.js” in the client folder.

import React from 'react';import { render } from 'react-dom';import App from './app';render(<App />, document.getElementById('root'));

Step9

Create your first app component

create a subfolder named “app” inside the client and add “index.js” file to the “app”.

import React from 'react';import { hot } from 'react-hot-loader/root';const App = () => {  return <div>Hello MERN</div>;};export default hot(App);

Step10

Setting up the server folder for backend

create a subfolder called “server” in the project folder.

create a file named “devBundle.js” in the “server” folder.

import webpack from 'webpack'import webpackMiddleware from 'webpack-dev-middleware'import webpackHotMiddleware from 'webpack-hot-middleware'import webpackConfig from './../webpack.config.client.js'const compile = (app) => {  if(process.env.NODE_ENV == "development"){    const compiler = webpack(webpackConfig)    const middleware = webpackMiddleware(compiler, {    publicPath: webpackConfig.output.publicPath  })  app.use(middleware)  app.use(webpackHotMiddleware(compiler))  }}export default {compile}

Bundling React code with webpack configuration during development mode.

We use webpack to compile client code when the server is running. Compile method uses Express app to configure webpack middleware to compile, bundle and serve code, and enables hot reloading in development mode.

Step11

Setting up the server to serve static files and configuring MongoDB client.

create a subfolder called “server” in the project folder.

create a file called “index.js” in the client folder.

import path from 'path'import express from 'express'import { MongoClient } from 'mongodb'import template from '../template'import devBundle from './devBundle'const app = express()if (process.env.NODE_ENV === 'development') {  devBundle.compile(app)}const CURRENT_WORKING_DIR = process.cwd()app.use('/dist', express.static(path.join(CURRENT_WORKING_DIR, 'dist')))app.get('/', (req, res) => {  res.status(200).send(template())})let port = process.env.PORT || 3000app.listen(port, function onStart(err) {  if (err) {    console.log(err)  }  console.info('Server started on port %s.', port)})try {  const url = process.env.MONGODB_URI || 'mongodb://localhost:27017/mernSetup'  MongoClient.connect(url, { useNewUrlParser: true }, (err, db) => {    if (err) {      return    }    console.log("Connected successfully to mongodb server")    db.close()  })} catch (e) {
console.log(e)
}

--

--