Miscellaneous MERN (MongoDB / Express / React / NodeJS) snippets

October 14, 2020

TOC

  • JWT generation
  • Setup Express Middleware for protected routes
  • Setup concurrently to run both Express backend and React frontend simultaneously
  • React Private route

JWT generation

JWT.io

E.g.: in an Express route when creating a user this is how you can generate a JWT (JSON Web Token)

import express from "express"
import jwt from 'jsonwebtoken'
import config from 'config'


const router = express.Router()

export default router.post(
  "/",
  async (req, res) => {
    .
    .
    .
    // assume we have a user object with an id attribute
    // and in /config/default.json: { "jwtSecret": "......" }

    const payload = {
      user: {
        id: user.id
      }
    }

    jwt.sign(
      payload,
      config.get("jwtSecret"),
      {
        expiresIn: 3600,
      },
      (err, token) => {
        if (err) {
          throw err
        } else {
          return res.json({ token })
        }
      }
  )
  }
  .
  .
  .

Setup Express Middleware for protected routes

Create a /middleware folder and an auth.js file in it.

import jwt from "jsonwebtoken"
import config from "config"

export default (req, res, next) => {
  jwt.verify(
    req.header("x-auth-token"),
    config.get("jwtSecret"),
    (err, decoded) => {
      if (err) {
        res.status(401).json({ msg: "Token invalid" })
      } else {
        req.user = decoded.user
        next()
      }
    }
  )
}

Then import it in your route js and pass that (e.g.:authMiddleware) as a 2nd parameter in your route, that you want to protect:

import express from "express"
import User from "../models/User.js"
import jwt from "jsonwebtoken"
import config from "config"
import authMiddleware from "../middleware/auth.js"

const router = express.Router()

// @route   GET api/auth
// @desc    get logged in user
// @access  private

// we brought in the middleware to protect the route
// and passed in as the 2nd parameter to the route.get
router.get("/", authMiddleware, async (req, res) => {
  try {
    const user = await User.findById(req.user.id).select("-password")
    return res.json(user)
  } catch (error) {
    console.log(error)
    res.status(500).send("Server error")
  }
})

Setup concurrently to run both Express backend and React frontend simultaneously

We have our Express backend server installed in a directory e.g.: reactContacts and then we use create-react-app to setup our frontend in the client directory under reactContacts (e.g.: npx create-react-app client). We want to run both Express and the React dev server at the same time.

We could do it in two different shells, but concurrently (installed already with npm) offers us a simple way to make it one npm run call.

In the main (Express) package.json make the scripts section as follows:

  "scripts": {
    "start": "node server.js",
    "server": "nodemon server.js",
    "client": "npm start --prefix client",
    "clientinstall": "npm i  --prefix client",
    "dev": "concurrently \"npm run server\" \"npm run client\" "
  },

We already had start and server for the Express server and we added:

  • client - to start the React webserver
  • clientsinstall - to invoke npm install for the React app
  • dev - to use concurrently to start both the Express and the React webservers at the same time

Additionally we can add a proxy, it’ll make sure that in our React http calls we can simply refer to routes as “/this/isa/route” instead of always prefixing with http://localhost:5000.

This is how you do it in your Express package.json:

...
  "proxy" : "http://localhost:5000"
...

React Private route

import React, { useContext } from "react"
import { Route, Redirect } from "react-router-dom"
import AuthContext from "../../context/auth/authContext"

const PrivateRoute = ({ component: Component, ...rest }) => {
  const authContext = useContext(AuthContext)
  const { isAuthenticated, loading } = authContext
  return (
    // Show the component only when the user is logged in
    // Otherwise, redirect the user to /signin page
    <Route
      {...rest}
      render={(props) =>
        !isAuthenticated && !loading ? (
          <Component {...props} />
        ) : (
          <Redirect to='/login' />
        )
      }
    />
  )
}

export default PrivateRoute

Hope this helps, cheers! emoji-thumbsup