Server-Side Sessions

The full project code for the example code listed on this webpage can be downloaded from this link.

Server-side sessions are needed if a user needs to be logged in in order to access data on the server-side, such as database data. Server-side sessions can be used to hold user login or other information.

 

Session ID

Session IDs need to be held in a cookie between each call from the client to the server. In order for the axios() method calls to retain the session ID, we need to include the line of code that is shown below before we make each axios call.

axios.defaults.withCredentials = true

The cookie will only hold the session ID. It will never hold any other data (such as a user-name or password) that is stored in the session. Therefore, the username, password, et cetera will be private to the server-side and cannot be seen from the client-side.

Client-Side

Other than the inclusion of axios.defaults.withCredentials = true before each axios() method, the client-side code does not change.

client/components/Register.js

import React, {Component} from "react"
import {Redirect, Link} from "react-router-dom"
import axios from "axios"

import LinkInClass from "../components/LinkInClass"

import {SERVER_HOST} from "../config/global_constants"


export default class Register extends Component
{
    constructor(props)
    {
        super(props)
        
        this.state = {
            name:"",
            email:"",
            password:"",
            confirmPassword:"",    
            isRegistered:false
        } 
    }
    
    
    handleChange = (e) => 
    {
        this.setState({[e.target.name]: e.target.value})
    }
    
    
    handleSubmit = (e) => 
    {
        e.preventDefault()

        axios.defaults.withCredentials = true // needed for sessions to work
        axios.post(`${SERVER_HOST}/register/${this.state.name}/${this.state.email}/${this.state.password}`)
        .then(res => 
        {     
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else // user successfully registered
                { 
                    console.log("User registered and logged in")
                    
                    sessionStorage.name = res.data.name
                    sessionStorage.accessLevel = res.data.accessLevel
                    
                    this.setState({isRegistered:true})
                }        
            }
            else
            {
                console.log("Registration failed")
            }
        })   
    }


    render() 
    {     
        return (
            <form className="form-container" noValidate = {true} id = "loginOrRegistrationForm">
           
                {this.state.isRegistered ? <Redirect to="/DisplayAllCars"/> : null} 
            
                <h2>New User Registration</h2>
           
                <input  
                    name = "name"              
                    type = "text"
                    placeholder = "Name"
                    autoComplete="name"
                    value = {this.state.name}
                    onChange = {this.handleChange}
                    ref = {(input) => { this.inputToFocus = input }} 
                /><br/>           

	        <input  
                    name = "email"              
                    type = "email"
                    placeholder = "Email"
                    autoComplete="email"
                    value = {this.state.email}
                    onChange = {this.handleChange}
                /><br/>              

	        <input  
                    name = "password"           
                    type = "password"
                    placeholder = "Password"
                    autoComplete="password"
                    title = "Password must be at least ten-digits long and contains at least one lowercase letter, one uppercase letter, one digit and one of the following characters (£!#€$%^&*)"
                    value = {this.state.password}
                    onChange = {this.handleChange}
                /><br/>           

                <input          
                    name = "confirmPassword"    
                    type = "password"
                    placeholder = "Confirm password"
                    autoComplete="confirmPassword"
                    value = {this.state.confirmPassword}
                    onChange = {this.handleChange}
                /><br/><br/>
                
                <LinkInClass value="Register New User" className="green-button" onClick={this.handleSubmit} />
                <Link className="red-button" to={"/DisplayAllCars"}>Cancel</Link>   
            </form>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/Login.js

import React, {Component} from "react"
import {Redirect, Link} from "react-router-dom"
import axios from "axios"

import LinkInClass from "../components/LinkInClass"
import {SERVER_HOST} from "../config/global_constants"


export default class Login extends Component
{
    constructor(props)
    {
        super(props)
        
        this.state = {
            email:"",
            password:"",
            isLoggedIn:false
        }
    }
    
    
    handleChange = (e) => 
    {
        this.setState({[e.target.name]: e.target.value})
    }
    
    
    handleSubmit = (e) => 
    {
        axios.defaults.withCredentials = true // needed for sessions to work
        axios.post(`${SERVER_HOST}/login/${this.state.email}/${this.state.password}`)
        .then(res => 
        {     
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else // user successfully logged in
                { 
                    console.log("User logged in")
                    
                    sessionStorage.name = res.data.name
                    sessionStorage.accessLevel = res.data.accessLevel
                    
                    this.setState({isLoggedIn:true})
                }        
            }
            else
            {
                console.log("Login failed")
            }
        })                
    }


    render()
    {            
        return (
            <form className="form-container" noValidate = {true} id = "loginOrRegistrationForm">
                <h2>Login</h2>
                
                {this.state.isLoggedIn ? <Redirect to="/DisplayAllCars"/> : null} 
                
                <input 
                    type = "email" 
                    name = "email" 
                    placeholder = "Email"
                    autoComplete="email"
                    value={this.state.email} 
                    onChange={this.handleChange}
                /><br/>
                    
                <input 
                    type = "password" 
                    name = "password" 
                    placeholder = "Password"
                    autoComplete="password"
                    value={this.state.password} 
                    onChange={this.handleChange}
                /><br/><br/>
                
                <LinkInClass value="Login" className="green-button" onClick={this.handleSubmit}/> 
                <Link className="red-button" to={"/DisplayAllCars"}>Cancel</Link>                                      
            </form>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/Logout.js

import React, {Component} from "react"
import {Redirect} from "react-router-dom"
import axios from "axios"

import LinkInClass from "../components/LinkInClass"
import {ACCESS_LEVEL_GUEST, SERVER_HOST} from "../config/global_constants"


export default class Logout extends Component
{
    constructor(props)
    {
        super(props)
        
        this.state = {
            isLoggedIn:true
        }
    }
    
    
    handleSubmit = (e) => 
    {
        e.preventDefault()
        
        axios.defaults.withCredentials = true // needed for sessions to work
        axios.post(`${SERVER_HOST}/logout`)
        .then(res => 
        {     
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else
                { 
                    console.log("User logged out")
                    sessionStorage.clear() 

                    sessionStorage.name = "GUEST"
                    sessionStorage.accessLevel = ACCESS_LEVEL_GUEST
                    this.setState({isLoggedIn:false}) 
                }
            }
            else
            {
                console.log("Logout failed")
            }
        }) 
    }


    render()
    {
        return (
            <div>   
        
                {!this.state.isLoggedIn ? <Redirect to="/DisplayAllCars"/> : null} 
                  
                <LinkInClass value="Log out" className="red-button" onClick={this.handleSubmit}/> 
            </div>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/DisplayAllCars.js

import React, {Component} from "react"
import {Link} from "react-router-dom"

import axios from "axios"

import CarTable from "./CarTable"
import Logout from "./Logout"

import {ACCESS_LEVEL_GUEST, ACCESS_LEVEL_ADMIN, SERVER_HOST} from "../config/global_constants"


export default class DisplayAllCars extends Component 
{
    constructor(props) 
    {
        super(props)
        
        this.state = {
            cars:[]
        }
    }
    
    
    componentDidMount() 
    {
        axios.defaults.withCredentials = true // needed for sessions to work
        axios.get(`${SERVER_HOST}/cars/`)
        .then(res => 
        {
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else
                {           
                    console.log("Records read")   
                    this.setState({cars: res.data}) 
                }   
            }
            else
            {
                console.log("Record not found")
            }
        })
    }

  
    render() 
    {   
        return (           
            <div className="form-container">
                {sessionStorage.accessLevel >  ACCESS_LEVEL_GUEST ? 
                    <div className="logout">
                        <Logout/>
                    </div>
                :
                    <div>
                        <Link className="green-button" to={"/Login"}>Login</Link>
                        <Link className="blue-button" to={"/Register"}>Register</Link>  
                        <Link className="red-button" to={"/ResetDatabase"}>Reset Database</Link>  <br/><br/><br/></div>
                }
                
                <div className="table-container">
                    <CarTable cars={this.state.cars} /> 
                        
                    {sessionStorage.accessLevel >= ACCESS_LEVEL_ADMIN ?
                        <div className="add-new-car">
                            <Link className="blue-button" to={"/AddCar"}>Add New Car</Link>
                        </div>
                    :
                        null
                    }
                </div>
            </div> 
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/AddCar.js

import React, {Component} from "react"
import {Redirect, Link} from "react-router-dom"
import Form from "react-bootstrap/Form"

import axios from "axios"

import LinkInClass from "../components/LinkInClass"

import {ACCESS_LEVEL_ADMIN, SERVER_HOST} from "../config/global_constants"


export default class AddCar extends Component
{
    constructor(props)
    {
        super(props)

        this.state = {
            model:"",
            colour:"",
            year:"",
            price:"",
            redirectToDisplayAllCars:sessionStorage.accessLevel < ACCESS_LEVEL_ADMIN
        }
    }


    componentDidMount() 
    {     
        this.inputToFocus.focus()        
    }
 
 
    handleChange = (e) => 
    {
        this.setState({[e.target.name]: e.target.value})
    }


    handleSubmit = (e) => 
    {
        e.preventDefault()

        const carObject = {
            model: this.state.model,
            colour: this.state.colour,
            year: this.state.year,
            price: this.state.price
        }

        axios.defaults.withCredentials = true // needed for sessions to work
        axios.post(`${SERVER_HOST}/cars`, carObject)
        .then(res => 
        {   
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else
                {   
                    console.log("Record added")
                    this.setState({redirectToDisplayAllCars:true})
                } 
            }
            else
            {
                console.log("Record not added")
            }
        })
    }


    render()
    {        
        return (
            <div className="form-container"> 
                {this.state.redirectToDisplayAllCars ? <Redirect to="/DisplayAllCars"/> : null}                                            
                    
                <Form>
                    <Form.Group controlId="model">
                        <Form.Label>Model</Form.Label>
                        <Form.Control ref = {(input) => { this.inputToFocus = input }} type="text" name="model" value={this.state.model} onChange={this.handleChange} />
                    </Form.Group>
    
                    <Form.Group controlId="colour">
                        <Form.Label>Colour</Form.Label>
                        <Form.Control type="text" name="colour" value={this.state.colour} onChange={this.handleChange} />
                    </Form.Group>
    
                    <Form.Group controlId="year">
                        <Form.Label>Year</Form.Label>
                        <Form.Control type="text" name="year" value={this.state.year} onChange={this.handleChange} />
                    </Form.Group>
    
                    <Form.Group controlId="price">
                        <Form.Label>Price</Form.Label>
                        <Form.Control type="text" name="price" value={this.state.price} onChange={this.handleChange} />
                    </Form.Group> 
            
                    <LinkInClass value="Add" className="green-button" onClick={this.handleSubmit}/>            
            
                    <Link className="red-button" to={"/DisplayAllCars"}>Cancel</Link>
                </Form>
            </div>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/EditCar.js

import React, {Component} from "react"
import Form from "react-bootstrap/Form"
import {Redirect, Link} from "react-router-dom"
import axios from "axios"

import LinkInClass from "../components/LinkInClass"

import {ACCESS_LEVEL_NORMAL_USER, SERVER_HOST} from "../config/global_constants"

export default class EditCar extends Component 
{
    constructor(props) 
    {
        super(props)

        this.state = {
            model: ``,
            colour: ``,
            year: ``,
            price: ``,
            redirectToDisplayAllCars:sessionStorage.accessLevel < ACCESS_LEVEL_NORMAL_USER
        }
    }

    componentDidMount() 
    {      
        this.inputToFocus.focus()
  
        axios.defaults.withCredentials = true // needed for sessions to work
        axios.get(`${SERVER_HOST}/cars/${this.props.match.params.id}`)
        .then(res => 
        {     
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else
                { 
                    this.setState({
                        model: res.data.model,
                        colour: res.data.colour,
                        year: res.data.year,
                        price: res.data.price
                    })
                }
            }
            else
            {
                console.log(`Record not found`)
            }
        })
    }


    handleChange = (e) => 
    {
        this.setState({[e.target.name]: e.target.value})
    }


    handleSubmit = (e) => 
    {
        e.preventDefault()

        const carObject = {
            model: this.state.model,
            colour: this.state.colour,
            year: this.state.year,
            price: this.state.price
        }

        axios.defaults.withCredentials = true // needed for sessions to work
        axios.put(`${SERVER_HOST}/cars/${this.props.match.params.id}`, carObject)
        .then(res => 
        {             
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else
                {      
                    console.log(`Record updated`)
                    this.setState({redirectToDisplayAllCars:true})
                }
            }
            else
            {
                console.log(`Record not updated`)
            }
        })
    }


    render() 
    {
        return (
            <div className="form-container">
    
                {this.state.redirectToDisplayAllCars ? <Redirect to="/DisplayAllCars"/> : null}  
                        
                <Form>
                    <Form.Group controlId="model">
                        <Form.Label>Model</Form.Label>
                        <Form.Control ref = {(input) => { this.inputToFocus = input }} type="text" name="model" value={this.state.model} onChange={this.handleChange} />
                    </Form.Group>

                    <Form.Group controlId="colour">
                        <Form.Label>Colour</Form.Label>
                        <Form.Control type="text" name="colour" value={this.state.colour} onChange={this.handleChange} />
                    </Form.Group>

                    <Form.Group controlId="year">
                        <Form.Label>Year</Form.Label>
                        <Form.Control type="text" name="year" value={this.state.year} onChange={this.handleChange} />
                    </Form.Group>
        
                    <Form.Group controlId="price">
                        <Form.Label>Price</Form.Label>
                        <Form.Control type="text" name="price" value={this.state.price} onChange={this.handleChange} />
                    </Form.Group>
  
                    <LinkInClass value="Update" className="green-button" onClick={this.handleSubmit}/>  
    
                    <Link className="red-button" to={"/DisplayAllCars"}>Cancel</Link>
                </Form>
            </div>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

client/components/DeleteCar.js

import React, {Component} from "react"
import {Redirect} from "react-router-dom"
import axios from "axios"

import {SERVER_HOST} from "../config/global_constants"


export default class DeleteCar extends Component 
{
    constructor(props) 
    {
        super(props)
        
        this.state = {
            redirectToDisplayAllCars:false
        }
    }
    
    
    componentDidMount() 
    {   
        axios.defaults.withCredentials = true // needed for sessions to work
        axios.delete(`${SERVER_HOST}/cars/${this.props.match.params.id}`)
        .then(res => 
        {
            if(res.data)
            {
                if (res.data.errorMessage)
                {
                    console.log(res.data.errorMessage)    
                }
                else // success
                { 
                    console.log("Record deleted")
                }
                this.setState({redirectToDisplayAllCars:true})
            }
            else 
            {
                console.log("Record not deleted")
            }
        })
    }
  
  
    render() 
    {
        return (
            <div>   
                {this.state.redirectToDisplayAllCars ? <Redirect to="/DisplayAllCars"/> : null}                      
            </div>
        )
    }
}

All axios() methods must be preceeded by axios.defaults.withCredentials = true

axios.defaults.withCredentials = true // needed for sessions to work

Server-Side

A session's ID (which is shared on the client-side via a cookie) and its server-side details are accessible via the req.session object, which is one of the parameters of the various router methods. A session property is created and becomes available for subsequent use as soon as it is first assigned a value. In the "Cars" example, a session property called user has been assigned a sub-property called accessLevel.

server/.env

# This file holds global constants that are visible on the Server-side

# Database
DB_NAME = D01234567


# Access Levels
ACCESS_LEVEL_GUEST = 0
ACCESS_LEVEL_NORMAL_USER = 1
ACCESS_LEVEL_ADMIN = 2


# Keys
SESSION_PRIVATE_KEY = yourRandomKey


# Salt length of encryption of user passwords
# The salt length should be 16 or higher for commercially released code
# It has been set to 3 here, so that the password will be generated faster
PASSWORD_HASH_SALT = 3


# Port
SERVER_PORT = 4000


# Local Host
LOCAL_HOST = http://localhost:3000

We create a private SESSION_PRIVATE_KEY for our session.

server/server.js

// Server-side global variables
require(`dotenv`).config({path:`./config/.env`})


// Database
require(`./config/db`)


// Express
const express = require(`express`)
const app = express()
app.use(require(`express-session`)({
    secret: process.env.SESSION_PRIVATE_KEY,
    resave: false,
    cookie: {secure: false, maxAge: 60000}, 
    saveUninitialized: true
}))

app.use(require(`body-parser`).json())
app.use(require(`cors`)({credentials: true, origin: process.env.LOCAL_HOST}))


// Routers
app.use(require(`../server/routes/cars`))
app.use(require(`../server/routes/users`))


// Port
app.listen(process.env.SERVER_PORT, () => 
{
    console.log(`Connected to port ` + process.env.SERVER_PORT)
})


// Error 404
app.use((req, res, next) => {next(createError(404))})

// Other errors
app.use(function (err, req, res, next)
{
    console.error(err.message)
    if (!err.statusCode) 
    {
        err.statusCode = 500
    }
    res.status(err.statusCode).send(err.message)
})

We use the express-session package.

app.use(require(`express-session`)({
    secret: process.env.SESSION_PRIVATE_KEY,
    resave: false,
    cookie: {secure: false, maxAge: 60000}, 
    saveUninitialized: true
}))

Install the express-session package.

server/routes/users.js

const router = require(`express`).Router()

const usersModel = require(`../models/users`)

const bcrypt = require('bcrypt');  // needed for password encryption


// IMPORTANT
// Obviously, in a production release, you should never have the code below, as it allows a user to delete a database collection
// The code below is for development testing purposes only 
router.post(`/reset_user_collection`, (req,res) => 
{
    usersModel.deleteMany({}, (error, data) => 
    {
        if(data)
        {
            const adminPassword = `123!"£qweQWE`
            bcrypt.hash(adminPassword, parseInt(process.env.PASSWORD_HASH_SALT), (err, hash) =>  
            {
                if(err){console.log("bad")}
                usersModel.create({name:"Administrator",email:"admin@admin.com",password:hash,accessLevel:parseInt(process.env.ACCESS_LEVEL_ADMIN)}, (createError, createData) => 
                {
                    if(createData)
                    {
                        res.json(createData)
                    }
                    else
                    {
                        res.json({errorMessage:`Failed to create Admin user for testing purposes`})
                    }
                })
            })
            
        }
        else
        {
            res.json({errorMessage:`User is not logged in`})
        }
    })      
})


router.post(`/register/:name/:email/:password`, (req,res) => 
{
    // If a user with this email does not already exist, then create new user
    usersModel.findOne({email:req.params.email}, (uniqueError, uniqueData) => 
    {
        if(uniqueData)
        {
            res.json({errorMessage:`User already exists`})
        }
        else
        {
            bcrypt.hash(req.params.password, parseInt(process.env.PASSWORD_HASH_SALT), (err, hash) =>  
            {
                usersModel.create({name:req.params.name,email:req.params.email,password:hash}, (error, data) => 
                {
                    if(data)
                    {
                        req.session.user = {email: data.email, accessLevel:data.accessLevel}
                        res.json({name: data.name, accessLevel:data.accessLevel})
                    }
                    else
                    {
                        res.json({errorMessage:`User was not registered`})
                    }
                }) 
            })
        }
    })         
})
 

router.post(`/login/:email/:password`, (req,res) => 
{
    usersModel.findOne({email:req.params.email}, (error, data) => 
    {
        if(data)
        {
            bcrypt.compare(req.params.password, data.password, (err, result) =>
            {
                if(result)
                {
                    req.session.user = {email: data.email, accessLevel:data.accessLevel}
                    res.json({name: data.name, accessLevel:data.accessLevel})
                }
                else
                {
                    res.json({errorMessage:`User is not logged in`})
                }
            })
        }
        else
        {
            console.log("not found in db")
            res.json({errorMessage:`User is not logged in`})
        } 
    })
})


router.post(`/logout`, (req,res) => 
{       
    req.session.destroy()
    res.json({})
})


module.exports = router

When a user registers, their email and accessLevel are stored in the session. This session information will then be available on the server-side for other methods to access.

router.post(`/register/:name/:email/:password`, (req,res) => 
{
    // If a user with this email does not already exist, then create new user
    usersModel.findOne({email:req.params.email}, (uniqueError, uniqueData) => 
    {
        if(uniqueData)
        {
            res.json({errorMessage:`User already exists`})
        }
        else
        {
            bcrypt.hash(req.params.password, parseInt(process.env.PASSWORD_HASH_SALT), (err, hash) =>  
            {
                usersModel.create({name:req.params.name,email:req.params.email,password:hash}, (error, data) => 
                {
                    if(data)
                    {
                        req.session.user = {email: data.email, accessLevel:data.accessLevel}
                        res.json({name: data.name, accessLevel:data.accessLevel})
                    }
                    else
                    {
                        res.json({errorMessage:`User was not registered`})
                    }
                }) 
            })
        }
    })         
})

Likewise, when a user logs in, their email and accessLevel are stored in the session. This session information will then be available on the server-side for other methods to access.

router.post(`/login/:email/:password`, (req,res) => 
{
                    ...

                    req.session.user = {email: data.email, accessLevel:data.accessLevel}
                    
                    ...
})

When a user logs out, the session is destroyed and it is no longer accessible on the server-side.

router.post(`/logout`, (req,res) => 
{       
    req.session.destroy()
    res.json({})
})

server/routes/cars.js

const router = require(`express`).Router()

const carsModel = require(`../models/cars`)


// read all records
router.get(`/cars/`, (req, res) => 
{   
    //user does not have to be logged in to see car details
    carsModel.find((error, data) => 
    {
        res.json(data)
    })
})


// Read one record
router.get(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        carsModel.findById(req.params.id, (error, data) => 
        {
            res.json(data)
        })
    }
})


// Add new record
router.post(`/cars/`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)
        {
            carsModel.create(req.body, (error, data) => 
            {
                res.json(data)
            })
        }
        else
        {
            res.json({errorMessage:`User is not an administrator, so they cannot add new records`})
        }
    }
})


// Update one record
router.put(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        carsModel.findByIdAndUpdate(req.params.id, {$set: req.body}, (error, data) => 
        {
            res.json(data)
        })        
    }
})


// Delete one record
router.delete(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)
        {
            carsModel.findByIdAndRemove(req.params.id, (error, data) => 
            {
                res.json(data)
            })
        }
        else
        {
            res.json({errorMessage:`User is not an administrator, so they cannot delete records`})
        }        
    }
})

module.exports = router

A session is only set up when a user successfully logs in.

We can test if a user is logged in by checking if(typeof req.session.user === `undefined`)

// Read one record
router.get(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        carsModel.findById(req.params.id, (error, data) => 
        {
            res.json(data)
        })
    }
})

We can test if a user is logged in by checking if(typeof req.session.user === `undefined`)

We can test the accessLevel of a user by checking if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)

// Add new record
router.post(`/cars/`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)
        {
            carsModel.create(req.body, (error, data) => 
            {
                res.json(data)
            })
        }
        else
        {
            res.json({errorMessage:`User is not an administrator, so they cannot add new records`})
        }
    }
})

We can test if a user is logged in by checking if(typeof req.session.user === `undefined`)

// Update one record
router.put(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        carsModel.findByIdAndUpdate(req.params.id, {$set: req.body}, (error, data) => 
        {
            res.json(data)
        })        
    }
})

We can test if a user is logged in by checking if(typeof req.session.user === `undefined`)

We can test the accessLevel of a user by checking if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)

// Delete one record
router.delete(`/cars/:id`, (req, res) => 
{
    if(typeof req.session.user === `undefined`)
    {
        res.json({errorMessage:`User is not logged in`})
    }
    else
    {
        if(req.session.user.accessLevel >= process.env.ACCESS_LEVEL_ADMIN)
        {
            carsModel.findByIdAndRemove(req.params.id, (error, data) => 
            {
                res.json(data)
            })
        }
        else
        {
            res.json({errorMessage:`User is not an administrator, so they cannot delete records`})
        }        
    }
})

Write code to incorporate the login (from here), registration (from here) and validation (from here), as shown here.

Add MongoDB field validation to the above code. You should provide appropriate client-side error messages.

Why should you validate at all three of the client-side, the server-side routes, and the mongoDB?

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>