Search code examples
node.jsexpressapi-design

Node JS running but not responding. Works but then periodically stops responding


I've created my first ever NodeJS Api (first time JS too) and so am coming up against a few issues and was really hoping for some help/direction.

My current issue is that the API works however every now and then (more than once a day) it stops responding. (testing using postman). It doesnt respond with 'no response' it just keeps trying as if waiting for a response.

When I log into the node i use:

lsof -i tcp:3000
COMMAND   PID  USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
node    22361 [myserver] 18u  IPv6 190588033      0t0  TCP *:hbci (LISTEN)
kill -9 22361

When I kill it, i wait 10 seconds and it starts up again because i have installed FOREVER.

The issue is that it seems the node is there just... not functioning. If it crashed, wouldn't it restart and be working again but instead it is just "there".

How can I diagnose this?

I do also have Nodemon installed but can't really get it working properly as I get an EAINUSE error

  • I have created the file on my VPS.

  • SSH using Visual Stdio Code on my Win10 PC.

  • Watched a few youtube vids to get me running

  • My main js file is very basic:

js file:

const app = require('./app');
const port = process.env.port || 3000;
app.listen(port);

At the moment I can't seem to find "why" the node goes from working and responsive to stagnant, running but not actually working!

Happy to share code, just have about 12 js files and didn't want to throw too much on here.

Package.json:

{
  "name": "qudaapi",
  "version": "1.0.0",
  "description": "NodeJSAPI",
  "main": "qudaserver.js",
  "scripts": {
    "start": "node qudaserver.js"
      },
  "author": "GAngel",
  "license": "ISC",
  "dependencies": {   
    "bcryptjs": "^2.4.3",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "morgan": "^1.10.0",
    "mysql": "^2.18.1"
  }
}

App.js

const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');

const app = express();

app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

app.use((req,res,next)=>{
    res.header("Access-Control-Allow-Origin","*");
    res.header("Access-Control-Allow-Headers","Origin,X-Requested-With,Content-Type,Accept,Authorization");

    if (req.method === 'OPTIONS'){
        res.header('Access-Control-Allow-Methods','PUT,POST,PATCH,DELETE,GET');
        return res.status(200).json({});
    }
    next();
});

//Import Routes
const chemistRoutes = require('./api/routes/chemists');
const smsHxRoutes = require('./api/routes/smsHx');
const authRoute = require('./api/routes/auth');
const webhookRoutes = require('./api/routes/stripehook');
const orderRoutes = require('./api/routes/orders');
const comboDataRoutes = require('./api/routes/comboData');
const staffRoutes = require('./api/routes/staff');
const orderListsRoutes = require('./api/routes/orderLists');
const contactLogRoutes = require('./api/routes/contactLog');
const licenseRoutes = require('./api/routes/license');

//Route Middleware
app.use('/smsHx',smsHxRoutes);
app.use('/chemists',chemistRoutes);
app.use('/register',authRoute);
app.use('/stripehook',webhookRoutes);
app.use('/orders',orderRoutes);
app.use('/comboData',comboDataRoutes);
app.use('/staff',staffRoutes);
app.use('/orderLists',orderListsRoutes);
app.use('/contactLog',contactLogRoutes);
app.use('/license',licenseRoutes);



app.use((req,res,next) => {
    const error = new Error('Endpoint not Found');
    error.status = 404;
    next(error);
})

app.use((error,req,res,next) => {
res.status(error.status || 500);
res.json({
    error: {
        message: error.message
    }
    });

});


module.exports = app;

Section that is causing some kind of failed loop:

//Login_Get APIkey
router.post('/login',verifyQUDA,async (req,res) => {
    let loginChemist = req.body;
    const realPass = loginChemist.chemistPassword;
    
    // CHECK Password  
   
    var sqlString = "SELECT * From tblChemists WHERE userName = ?;";

        connection.query(sqlString,[loginChemist.userName], async (err,rows,fields)=>{
            if (rows && Array.isArray(rows) && rows.length) {

                    const savedHash = rows[0].chemistpass;
                    const chemistID = rows[0].chemistID;
                    const validPass = await bcrypt.compare(realPass,savedHash);
                    

                    if(!validPass){
                        return res.status(200).json({
                            Result: false
                                })
                    }else{

                        const token = jwt.sign({_id: chemistID},process.env.TOKEN_SECRET);
                        res.header('auth-token',token);
                        return res.status(200).json({
                            Result: true,
                            API_Token: token
                                })
                    } 

            }       
            })
        
        
        

})

So my code runs and I can use the API, I seem to be able to use it fine (all routes) and get the expected responses, however if I log in randomly and just do a test, sometimes it is down and POSTMAN just keeps spinning waiting for a response but when I check... it is still 'running'

Really keen to know what I should do to diagnose?

EDIT: The code above is the section which sometimes loops or causes issues. I have found that the api is in fact running as other API calls are working AND this API call "works" if I do not add the header. Therefore I can get responses 500 if no header is there, or if the header API Key is incorrect.

The issue seems to be if the header is correct, and user and pass are correct sometimes I just get a looped response... The code DOES work however and give the API_Token I need here and there but sometimes just loops like a nutcase!


Solution

  • Your /login handler has numerous code paths that don't send any response and is missing some error handling, so you could end up with "silent" errors and hung requests. Personally, I'd switch over to the promise API on your database, but without making that structural change, here's one way of at least making sure you always send a response and catch errors:

    //Login_Get APIkey
    router.post('/login', verifyQUDA, (req, res) => {
        const loginChemist = req.body;
        // CHECK Password
        var sqlString = "SELECT * From tblChemists WHERE userName = ?;";
        connection.query(sqlString, [loginChemist.userName], async (err, rows, fields) => {
            try {
                if (err) {
                    // catch database errors
                    console.log(err);
                    res.sendStatus(500);
                } else if (rows && rows.length) {
                    const savedHash = rows[0].chemistpass;
                    const chemistID = rows[0].chemistID;
                    const realPass = loginChemist.chemistPassword;
                    const validPass = await bcrypt.compare(realPass, savedHash);
                    if (!validPass) {
                        res.status(200).json({ Result: false });
                    } else {
                        const token = jwt.sign({ _id: chemistID }, process.env.TOKEN_SECRET);
                        res.header('auth-token', token);
                        res.status(200).json({ Result: true, API_Token: token });
                    }
    
                } else {
                    // send some status here when the username does not exist
                    // or your rows condition is not met
                    res.sendStatus(404);
                }
            } catch (e) {
                // this catches any exception in this scope or await rejection
                console.log(e);
                res.sendStatus(500);
            }
        });
    });