How to share/store data between routes in express (nodejs)?

Viewed 627

I'll go in detail on what I really want my server to do. So basically, I'm creating a project in which the user register and then logs in.

The routes are like - localhost:3000/login and localhost:3000/register

Now when the user logs in using their credentials (POST) , it should send them to localhost:3000/home which is unique for every person. I don't want to send the data like we do for templating engines but I want to make that data accessible across all routes so that I can use it whenever I like.

What I'm having trouble with is that when the person logs in , their data gets stored in a session (which contains their name and other user data) which as of now is not sharable between routes. The session gets stored for the /login route and I'm unable to use it for the /home route or for any other route for that matter. Is there any way that I can use save a session every time a person logs in (using POST) and make that session data available across all my routes ?

server.js


var express = require('express');
const path = require('path');
const fs = require('fs');
const session = require('express-session');
const { v4: uuidv4 } = require('uuid');
const app = express();

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public/auth-pages')));



app.use(express.json());

var publicPath = __dirname+"/public"
var authPagesPath = publicPath+"/auth-pages"
var jsonPath = __dirname+"/json"
var usersFile = jsonPath+"/users.json"

var testVar = 1;


app.set('view engine', 'pug')
// have to be on top
app.use(logger)











app.get('/',(req,res) => {
    res.sendFile('index.html',{root:publicPath})

    
})

app.get('/home',(req,res) => {
    var data = {}
    res.render('home',data)
})

app.get('/profile',(req,res) => {
    var data = {}
    res.render('profile',data)
})

app.get('/login',(req,res) => {
    res.sendFile('login.html',{root:authPagesPath})

    
})

app.get('/register',(req,res) => {
    res.sendFile('register.html',{root:authPagesPath})
})


app.post('/register',(req,res) => {
    var data = req.body;

    if (register(data)){
        res.send({
            register:"success"
        })
    }

    else {
        res.send({
            register:"fail"
        })
    }

})




app.post('/login',(req,res) => {
    var data = req.body;
    fs.readFile(usersFile,(error,fullData) => {
        var fullData = JSON.parse(fullData);

        allUsernames = Object.keys(fullData);

        if (!allUsernames.includes(data.username)){
            res.send({
                login:"fail",
                reason:"invalid-username"
            })

        }
        else if (fullData[data.username]['pwd'] != data.pwd){
            res.send({
                login:"fail",
                reason:"wrong-pwd"
            })
        }
        else {
            res.send({
                login:"success",
                user:fullData[data.username]
            })
            // session storage 
            req.session.user = {
                username:data.username,
                id:fullData[data.username]['id']
            }
            console.log(req.session)
        }


    })

})





app.get('/test',(req,res) => {
    testVar += 1;
    res.send(""+testVar);
    
})







function register(data){
    fs.readFile(usersFile,(err,fullData) => {
        var fullData = JSON.parse(fullData)
        if (Object.keys(fullData).includes(data.username)){
            console.log('username taken')
            
        }
        else {
            fullData[data.username] = {
                id:uuidv4(),
                pwd:data.pwd
            }
            
            fullData = JSON.stringify(fullData,null,4)
            fs.writeFile(usersFile,fullData,error => {

            })
        
        }
    })





    return true;
}




2 Answers

You can use cookies to keep user data between routes.

If you dont want to store the whole data in the browser, you can keep the user id in cookies and store the whole data in a repository object.

for example: you can create a class that will store the state of the logged in users and can be reachable between routes. the class should be instantiate and you should export its object.

( this solution keeps the state in memory and will be lost when service restarts/shutdown. to make the state available after restarts you can store the state in db (redis, mongo etc...)).

repo class:

class MyRepository {
   constuctor() {
       this.users = {};
   }

   set(user) {
       this.users[user.id] = user;
   }

   get(userId) {
       return this.users[userId];
   }
}

let repo = new MyRepository();
module.exports = repo;

route:

const express = require('express');
const router = express.Router();
const repo = require('myrepository.js'); // this line will get you the object with all logged in users already

router.post('/login', (req, res, next) => {
  // check if user logged in and get the user id (to myUserId);
  user = ...
  logged = ...

  if (logged) {
      res.cookie('userId', user.id)
      repo.set(user)
  }
});

route.get('/home', (req, res, next) => {
      let userId = req.cookies.userId // get user id from cookie
      let user = repo.get(userId);
});

You can do this by having some sort of a class that holds user's data. Please be aware that this solution is not scalable and you are designing a single point of failure.
For instance, if you have multiple servers running your application with a load balancer that routes requests to your servers. Let's say Server A creates an object from a class for User A. In the second or third request, presume that User A's request gets routed to Server B, which has not created an object that holds User A's data. This leads to scalability and even perhaps inconsistency issues.

Related