node.js, express.js, and mysql - Cannot read property 'status' of undefined - javascript

I am new to node.js and am having an issue getting the data from mysql and sending to api response. Here is my setup:
db.js:
var mysql = require('mysql2');
var util = require('util')
const pool = mysql.createPool({
connectionLimit : 100, //important
host: 'AWSHost',
user: 'devadmin',
password: 'my password',
database: 'DB',
waitForConnections: true,
queueLimit: 0
})
pool.getConnection((err, connection) => {
if (err) {
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
console.error('Database connection was closed.')
}
if (err.code === 'ER_CON_COUNT_ERROR') {
console.error('Database has too many connections.')
}
if (err.code === 'ECONNREFUSED') {
console.error('Database connection was refused.')
}
}
if (connection) {
console.log('DB connected, all good'),
connection.release()
return
}
})
pool.query = util.promisify(pool.query) // Magic happens here.
module.exports = pool
app.js module is:
const express = require('express');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const config = require('./src/lib/config');
const logger = require('./src/lib/logger');
const pool = require('./src/api/db/db')
const log = logger(config.logger);
const app = express();
const port = process.env.PORT || 8081;
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const swaggerDocument = YAML.load('./swagger.yaml');
var options = {
customCss: '.swagger-ui .topbar { display: none }'
};
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
/*
* Routes
*/
app.use('/1/user', require('./src/api/routes/user'));
// catch 404
app.use((req, res, next) => {
log.error(`Error 404 on ${req.url}.`);
res.status(404).send({ status: 404, error: 'Not found' });
});
// catch errors
app.use((err, req, res, next) => {
const status = err.status || 500;
const msg = err.error || err.message;
log.error(`Error ${status} (${msg}) on ${req.method} ${req.url} with payload ${req.body}.`);
res.status(status).send({ status, error: msg });
});
module.exports = app;
app.listen(port, () => console.log(`App listening on port ${port}!`))
Then I have a router:
user.js
/**
* Get user by usersID
*/
router.get('/:usersID', async (req, res, next) => {
debugger;
const options = {
usersID: req.params['usersID']
};
try {
const result = await user.getUserByUsersId(options);
res.status(result.status || 200).send(result.data);
} catch (err) {
next(err);
}
});
Then a Service:
/**
* #param {Object} options
* #param {Integer} options.usersID The usersID that needs to be fetched. Use usersID 1 for testing.
* #throws {Error}
* #return {Promise}
*/
module.exports.getUserByUsersId = async (options) => {
let sql = 'SELECT * FROM users';
pool.query(sql, (err, result,) => {
if (err) //throw err;
throw new ServerError({
status: 500, // Or another error code.
error: 'Server Error' // Or another error message.
});
return {
status: 200,
data: result,
}
}
)
}
The issue that I am having is that I am able to use console.log(result)and get the results from the DB to output to the console, but when using postman I get an error "Cannot read property 'status' of undefined".
,"msg":"Error 500 (Cannot read property 'status' of undefined) on GET /1/user/1 with payload [object Object]."
Can anyone help as to what I am doing wrong, or if I need an additional step to format the results prior to sending? Any help is appreciated, thank you!!

You can try to run your query inside your get router without calling your service with await function .
//dont forget to initialize your pool var
router.get('/:usersID', function(req, res, next){
debugger;
const options = {
usersID: req.params['usersID']
};
let sql = 'SELECT * FROM users';
pool.query(sql, (err, result) => {
if (err) {
console.log(err.message)
res.send(err.message)
return
}
res.send(result)
return
});
})
Your Service
module.exports.getUserByUsersId = function (options) {
return new Promise(resolve => {
let sql = 'SELECT * FROM users';
pool.query(sql, (err, result) => {
if (err) {
console.log(err.message)
resolve(err.message)
}
console.log(result)
resolve(result)
})
});
}

Your getUserByUsersId does not return the output of your query. Try this:
module.exports.getUserByUsersId = async (options) => {
let sql = 'SELECT * FROM users';
return new Promise((resolve, reject) => {
pool.query(sql, (err, result,) => {
if (err) {
//throw err;
reject(new ServerError({
status: 500, // Or another error code.
error: 'Server Error' // Or another error message.
}));
return
}
resolve({
status: 200,
data: result,
})
})
})
}
You don't technically need the async declaration anymore, but I like to keep it because it plays nicer with my IDE than just returning a promise (unless I include an annotation, but I digress).

Related

Middleware is breaking redis / express setup

I'm using redis for the first time, and I can't quite figure out why my middleware 'cache' function is breaking my code? It works great without it, displays in the browser, if I go to my terminal and check for a key value pair it works great.
Here is my setup:
const express = require("express");
const redis = require("redis");
const axios = require("axios").default;
const PORT = process.env.PORT || 5000;
const REDIS_PORT = process.env.PORT || 6379;
const client = redis.createClient(REDIS_PORT);
client.connect();
const app = express();
function setResponse(username, repos) {
return `<h2>${username} has ${repos} Github repos</h2>`;
}
// make req to github
async function getRepos(req, res, next) {
try {
console.log("fetching data...");
const { username } = req.params;
const response = await axios.get(
`https://api.github.com/users/${username}`
);
const data = response.data;
const repos = data.public_repos;
// set to redis
client.set(username, repos);
res.send(setResponse(username, repos));
} catch (err) {
console.log(err);
res.status(500);
}
}
// Cache middleware
function cache(req, res, next) {
const { username } = req.params;
client.get(username, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
});
}
app.get("/repos/:username", cache, getRepos);
app.listen(5000, () => {
console.log(`App listening on port ${PORT}`);
});
Any advice would be much appreciated!
Your cache function
function cache(req, res, next) {
const { username } = req.params;
client.get(username, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
});
}
uses node redis 3 "logic" so it's hanging
redis 4 is promise led, so you need to use async/await or .then/.catch
So something like this should work for the .then/.catch approach
// Cache middleware
function cache(req, res, next) {
console.log('caching for', req.params);
const { username } = req.params;
client.get(username)
.then((data) => {
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
})
.catch(err => {
if (err) throw err;
});
}
This will solve your initial problem of "why it's getting stuck"

Node error handler bugs when using error pattern (err, req, res, next)

I am currently running node with the express middleware and handling request with the GET method. Errors like 404s or 500s are handled at the end of my script, after the GET method, but so far I have not been able to use Node's standard error handling method with err,req,res,next. The below code works fine for me. But ...
server.js
...
// Routing
app.use('/', function (req, res, next) {
req.const = {
nonce: nonce,
publicRoot: publicRoot,
client: client,
cachelifespan: cachelifespan
};
next();
}, router);
// Error handling
app.use('/', function (req, res, next) {
req.const = {
nonce: nonce,
publicRoot: publicRoot,
};
next();
}, errorHandler);
...
errorhandler.js:
...
errorHandler.use(function (req, res) {
let id = req.ip;
let url = req.url;
// Import
let nonce = req.const.nonce;
let publicRoot = req.const.publicRoot;
res.statusCode = 500;
let status = res.statusCode;
try {
let localPath = path.join(publicRoot, req.path);
fs.accessSync(localPath);
} catch (err) {
status = 404;
};
if (req.accepts('html')) {
try {
res.render('system/error', { title: status.toString(), description: 'This is an error!', nonce: 'nonceValue' }, function(err, html) {
html = html.replace("nonceValue", nonce);
res.send(html);
});
} catch (err) {
logError(id, status, req.url, err)
};
} else if (req.accepts('json')) {
res.send({ error: status.toString() });
} else {
res.type('txt').send("error: " + status.toString());
}
logError(id, status, req.url);
});
...
... if I try to write the app.use function to ...
app.use(function (err, req, res, next) {
... it bugs and Node's default error handler takes over.
I am not entirely sure how to fix that.
Or if I am doing the error handling right?
Any suggestions?
I found a solution to my problem.
Inside server.js (the main app js), I load the errorHandler in the beginning of the file:
const errorHandler = require('./errorhandler');
And in the last step, after the basic routing, I added the errorhandler:
// Routing
app.use('/', function (req, res, next) {
req.const = {
nonce: nonce,
publicRoot: publicRoot,
client: client,
cachelifespan: cachelifespan
};
next();
}, router);
// Error handling
app.use('/', function (req, res, next) {
req.const = {
nonce: nonce,
publicRoot: publicRoot,
};
next();
}, errorHandler);
Inside the separate errorhandler file:
//--------------------
// NODE
//--------------------
const express = require('express');
const errorHandler = express.Router();
//--------------------
// LOGGING
//--------------------
const { getDate, logOutput, logError } = require('./logger');
//--------------------
// ROUTER
//--------------------
errorHandler.use(function (req, res) {
let id = req.ip;
let url = req.url;
// Import
let nonce = req.const.nonce;
let publicRoot = req.const.publicRoot;
// Cookies
let insultor = "Samuel L Jackson says";
let insults = ['Motherfucker!!!', 'Son_of_a_bitch!!!', 'Asshole!!!', 'Punkass_motherfucker!!!', 'Whiny_little_bitch!!!', 'You_deserve_to_die_and_I_hope_you_burn_in_hell!!!', 'Web_motherfucker_do_you_speak_it?'];
let number = Math.floor(Math.random() * insults.length);
let insulted = false;
for (let [key, value] of Object.entries(req.cookies)) {
if (key.toString() == insultor) {
value = value.toString() + "_-_" + insults[number];
res.cookie(insultor, value);
insulted = true;
}
}
if (!insulted) {
res.cookie(insultor, insults[number]);
}
res.statusCode = 500;
let status = res.statusCode;
try {
let localPath = path.join(publicRoot, req.path);
fs.accessSync(localPath);
} catch (err) {
status = 404;
};
if (req.accepts('html')) {
try {
res.render('system/error', { title: status.toString(), description: 'This is an error, Motherfucker!', nonce: 'nonceValue' }, function(err, html) {
html = html.replace("nonceValue", nonce);
res.send(html);
});
} catch (err) {
logError(id, status, req.url, err)
};
} else if (req.accepts('json')) {
res.send({ error: status.toString() });
} else {
res.type('txt').send("error: " + status.toString());
}
logError(id, status, req.url);
});
module.exports = errorHandler;

Node JS PATCH request running no matter what

Sorry if the title if confusing, I wasn't too sure how to word it. I have a PATCH request to update a value in my database, but even though it is "working" (200 status), it's not actually.
I have a .route('/:movie_id/:user_id').all() handler to trigger for all my methods, where it pulls a movie from the database by movie_id and user_id. This works. Then I move on to my PATCH request, but it seems like the PATCH request isn't actually running. I am getting the correct response from the .all() handler, but no update is happening. Even if I completely comment out the code for my PATCH, I am still getting a 200 status.
Here is my .all() handler with my PATCH request:
movieRouter
.route('/:movie_id/:user_id')
.all(requireAuth)
.get((req, res, next) => {
const db = req.app.get('db')
MovieService.getById(db, req.params.movie_id, req.params.user_id)
.then(movie => {
if(!movie) { // this runs fine
return res.status(404).json({ error: `Movie doesn't exist`})
}
// res.json({movie : movie}); --> old code
// solution:
res.movie = movie;
next();
return movie;
})
.catch(next)
})
.patch(requireAuth, (req, res, next) => {
const db = req.app.get('db')
const { watched } = req.body
const updatedMovie = { watched }
// this doesn't run
const numVal = Object.values(updatedMovie).filter(Boolean).length
if(numVal === 0) {
return res.status(400).json({ error: `Must not be blank`})
}
MovieService.updateMovie(db, req.params.movie_id, req.params.user_id, updatedMovie)
.then(movie => {
res.status(200).json(updatedMovie)
})
.catch(next)
})
Here is my MovieService:
updateMovie(db, movie_id, newMovie) {
return db('your_movie_list').where('id', movie_id).where('user_id', user_id).update(newMovie).returning('*')
}
It should be the problem of the 2nd .all(), .all() will catch all request, no matter it is GET, POST, PATCH, DELETE. So even when you comment out PATCH code, it will return 200.
Change the 2nd .all to .get like below
app.use(express.json())
movieRouter
.route('/:movie_id/:user_id')
.all(requireAuth)
.get((req, res, next) => { // use .get instead of .all to avoid catching all requests
const db = req.app.get('db')
MovieService.getById(db, req.params.movie_id, req.params.user_id)
.then(movie => {
if(!movie) { // this runs fine
return res.status(404).json({ error: `Movie doesn't exist`})
}
res.json({movie : movie});
})
.catch((e) => {
console.log("From getMovie", e);
res.status(400).json({ error: e.message })
})
})
.patch((req, res, next) => {
try {
const db = req.app.get('db')
const { watched } = req.body
const updatedMovie = { watched }
// this doesn't run
const numVal = Object.values(updatedMovie).filter(Boolean).length
if(numVal === 0) {
return res.status(400).json({ error: `Must not be blank`})
}
MovieService.updateMovie(db, req.params.movie_id, req.params.user_id, updatedMovie)
.then(movie => {
console.log(movie) // nothing logs
res.status(200).json(movie[0])
})
.catch((e) => {
console.log("From updateMovie", e);
res.status(400).json({ error: e.message })
})
}catch(e) {
console.log("From try/catch", e);
res.status(400).json({ error: e.message })
}
})
A little working example for cross-checking
const express = require("express");
const app = express();
const PORT = process.env.PORT || 8080;
app.use(express.json())
const movieRouter = express.Router()
movieRouter
.route('/:movie_id/:user_id')
// .all(requireAuth)
.get((req, res, next) => {
res.json({"movie:get" : 1});
})
.patch((req, res, next) => {
res.json({"movie:patch" : 1});
})
app.use(movieRouter)
app.listen(PORT, function (err) {
if (err) console.log(err);
console.log("Server listening on PORT", PORT);
});

"Sending request" loading on Get request on Postman

My /chat route works well through Post method with validation with Joi schema but when I send request through Get method, it show Sending Request and continue loading...
My index.js file:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const chat = require('./db/ChatModel');
const app = express();
app.use(bodyParser.json());
app.get('/chat', (req, res) => {
chat.getAllMessages().then( (messages) => {
res.json(messages);
});
});
app.post('/chat', (req, res) => {
console.log(req.dody);
chat.createMessages(req.body).then((message) => {
res.json(message);
}).catch( (error) => {
res.status(500);
res.json(error);
});
});
const port = process.env.PORT || 8888;
app.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
In connection.js I coded this
const monk = require('monk');
const connectionString = 'localhost/chatboard';
const db = monk(connectionString);
module.exports = db;
And ChatModal.js has the following code
const Joi = require('joi');
const db = require('./connection');
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(4).max(16).required(),
subject: Joi.string().required(),
message:Joi.string().max(300).required(),
imgUrl: Joi.string().uri({
scheme: [ // https://github.com/hapijs/joi/blob/v14.3.1/API.md#stringurioptions
/https?/
]
})
});
const chat = db.get('chat');
function getAllMessages() {
return chat.find();
};
function createMessages(message) {
const result = Joi.validate(message, schema);
if (result.error == null) {
message.created = new Date();
return chat.insert(message);
} else {
return Promise.reject(result.error);
}
}
module.exports = {
createMessages,
getAllMessages
};
I can't understand why getAllMessages() doesn't work and postman continue loading when Get request applied like this http://prntscr.com/s0d9c5
ChatModal.js
function getAllMessages() {
try {
return chat.find();
} catch (err) {
return next(err);
}
index.js
app.get('/chat', (req, res, next) => {
try{
data = chat.getAllMessages()
} catch (err) {
return next(error);
}
res.json(data);
});
User try-catch in the ChatModal.js and also index.js then you can understand what is actual error, like bellow:
ChatModal.js
function getAllMessages() {
try {
chat.find();
} catch (err) {
return next(err);
}
I think, may be your data, i mean message list data so weight, in this case you get all message,res.json(messages); json method have long time to parse messages data

API get call returns status 400

I'm trying to learn how to use the MERN stack. I've been following a YouTube tutorial (https://www.youtube.com/watch?v=WT67-OETeGU). Currently, I've defined and created my server.js file as follows:
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
const todoRoutes = express.Router();
const PORT = 4000;
let Todo = require("./todo.model.js");
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/todos", {
useUnifiedTopology: true,
useNewUrlParser: true
});
const connection = mongoose.connection;
todoRoutes.route("/").get(function(req, res) {
Todo.find(function(err, todos) {
if (err) {
console.log("error getting data");
} else {
res.json(todos);
}
});
});
todoRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (err) {
console.log("error finding todo object with id: " + id);
} else {
res.json(todo);
}
});
});
todoRoutes.route("/add").post(function(req, res) {
let todo = new Todo(req.body);
todo
.save()
.then(todo => {
res.status(200).json({ todo: "Todo added successfully" });
})
.catch(err => {
res.status(400).send("failed to save new todo");
});
});
todoRoutes.route("/update/:id").post(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (!todo) {
res.status(400).send("cant update id: " + id);
} else {
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
todo
.save()
.then(todo => {
res.json("Todo updated");
})
.catch(err => res.status(400).send("update not possible"));
}
});
});
app.use("./todos", todoRoutes);
I am trying to test my API out, so I installed Postman and set it up. I'm trying to make a GET request to: http://localhost:4000/todos. However, Postman only returns a 404, saying "Could not get any response".
I've run the commands mongod and mongo on my terminal to get Mongo running.
Any suggestions on where I've gone wrong?
It seems you are setting your endpoints as relative paths. Can you remove the dot proceeding "/todos"? in app.use("./todos", todoRoutes);
`

Categories