Search code examples
mysqlnode.jsexpressgoogle-cloud-platformgoogle-app-engine

Problem using Nodejs express-mysql-session store when connecting via socketPath from GCP App Engine standard


I am following these steps to connect to Google Cloud SQL from App Engine using Unix sockets. The connection works and I can retreive data from MySQL using "mysql2" npm module, but the server crashes when I try to use MySQL as an express session store using "express-mysql-session" npm module.

I searched online and in the documentation and I did not find any way to use socketPath with "express-mysql-session" and I also did not find any other MySQL session store that would support this connection. Do you know how I could use Cloud SQL as an express session store using Unix sockets from App Engine? I will use another session store like Firestore if not possible...

Thank you very much for your help!

This code works locally but not from App Engine:

const express = require("express");
const session = require("express-session");
const mysql = require('mysql2');
const MySQLStore = require('express-mysql-session')(session);

require("dotenv").config();

const app = express();

let mysqlConnexionOptions={
    user: process.env.DB_USER,
    password: process.env.DB_PASS, 
    database: process.env.DB_NAME, 
}

if(process.env.INSTANCE_UNIX_SOCKET){
    console.log("Found instance_unix_socket in yaml file, running from App engine");
    mysqlConnexionOptions.socketPath=process.env.INSTANCE_UNIX_SOCKET;
}else{
    console.log("Instance_unix_socket not found in yaml file, running locally");
    mysqlConnexionOptions.port=process.env.DB_PORT
    mysqlConnexionOptions.host=process.env.DB_HOST
}

const mysqlPool =mysql.createPool(mysqlConnexionOptions).promise();

const sessionStore = new MySQLStore(mysqlConnexionOptions);

app.use(session({
    secret:"test",
    name:"sid",
    cookie:{
        sameSite:"strict", // Prevents iFraming
        secure:false, // Set to true for https only access on production, false when testing locally
        httpOnly:true //prevent js to access it if true
    },
    store: sessionStore,
    resave:false,
    saveUninitialized:false
}));

app.get('/', async function (req, res) {
    req.session.test = "test!";
    console.log(req.session.test);
    const test=await mysqlPool.query("SHOW TABLES;"); 
    res.send(test[0][0].Tables_in_db+" "+req.session.test);
})

const PORT = process.env.PORT||8080;

app.listen(PORT,_=>{
    console.log(`Nodejs express app server listening on port ${PORT} http://localhost:${PORT}`);
});

This code works from App Engine because the session store was removed. So connection is good with "MySQL2" npm module:

const express = require("express");
const session = require("express-session");
const mysql = require('mysql2');

require("dotenv").config();

const app = express();

let mysqlConnexionOptions={
    user: process.env.DB_USER,
    password: process.env.DB_PASS, 
    database: process.env.DB_NAME, 
}

if(process.env.INSTANCE_UNIX_SOCKET){
    console.log("Found instance_unix_socket in yaml file, running from App engine");
    mysqlConnexionOptions.socketPath=process.env.INSTANCE_UNIX_SOCKET;
}else{
    console.log("Instance_unix_socket not found in yaml file, running locally");
    mysqlConnexionOptions.port=process.env.DB_PORT
    mysqlConnexionOptions.host=process.env.DB_HOST
}

const mysqlPool =mysql.createPool(mysqlConnexionOptions).promise();

app.get('/', async function (req, res) { 
    const test=await mysqlPool.query("SHOW TABLES;"); 
    res.send(test[0][0].Tables_in_db);
})

const PORT = process.env.PORT||8080;

app.listen(PORT,_=>{
    console.log(`Nodejs express app server listening on port ${PORT} http://localhost:${PORT}`);
});

Solution

  • I ran into the same problem and managed to solve it. express-mysql-session does not natively support passing in a socket. But there are two workarounds you can try out:

    1. Create a mysql connection using socket, and use this connection to initialize MysqlStore. See this.

    2. Use another library that supports socket. I chose connect-session-sequelize, which connects DB using sequelize. Sequelize is already adopted by my project to manage DB access, so it's a smooth switch for me. You create sequelize instance with socket like this:

        return new Sequelize(database, user, password, {
          host: host,
          dialect: dialect,
          dialectOptions: {
            socketPath: socket,
          },
        });
    

    then refer this to create session store with it.