Node JS PATCH request running no matter what - javascript

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);
});

Related

How to get session variable in app.post request?

I would like to get the data from session variable (req.user.username) then use it for posting. I'm using passportjs as authentication. I'm using router. Here is my code:
router.use('/login', passport.authenticate("local-register", async (err, user, info) => {
if (err) {
return next('Error');
}
if (!user) {
return next('Error');
}
req.user = user;
return req.login(user, (error: Error) => {
if (error) {
return next('Error');
}
return req.session.save((erro: Error) => {
if (erro) {
return next('Error');
}
return next();
});
});
})(req, res, next);)
router.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
router.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})
So I finally found the answer to the question. For those who will encounter the problem in the future. You just add the session middleware AGAIN on the top of the routes. If your routes are separated to the main server file.
/src/routes/routes.ts -> add again the middleware on top.
const app = router();
app.use(sessions) // -> right here you need to add the middleware again to //access the req.user session variable
app.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
app.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})

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 Express- delete route works but throws an error

I made an api app in Node Express. Routes post and put are working perfectly. Delete route is also working but I dont see confirmation of resolved promise, instead after few secs I see those errors
Access to XMLHttpRequest at 'https://ahi' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
PageAdmin.js:179 fail Error: Network Error
DELETE https://ahi net::ERR_FAILED
I was already dealing with that problem at beginning of setting up app on server so I searched how to deal with it. Here is my middleware code on server for setting up headers
function setHeaders(app) {
app.use(function (req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, PATCH, DELETE"
);
res.setHeader(
"Access-Control-Allow-Headers",
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin, Access-Control-Allow-Methods, x-auth-token"
);
res.setHeader("Access-Control-Allow-Credentials", true);
// handle OPTIONS method
if ("OPTIONS" == req.method) {
return res.sendStatus(200);
} else {
next();
}
});
}
Here is the code for routes
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const auth = require("../middleware/auth");
const { ProgrammingTool, validate } = require("../models/programmingTool");
const msg400 = "Bad request";
const msg400InData = "Item already exists in database";
const msg404 = "Could not find item in database";
const msg500 = "Something went wrong";
router.get("/", async (req, res) => {
try {
const tools = await ProgrammingTool.find();
res.send(tools);
} catch (e) {
console.log("failed getting tools", e);
res.status(500).send(msg500);
}
});
router.get("/:id", async (req, res) => {
if (!mongoose.Types.ObjectId.isValid(req.params.id))
return res.status(404).send(msg404);
const tool = await ProgrammingTool.findById(req.params.id);
if (!tool) return res.status(404).send(msg404);
res.send(tool);
});
router.post("/", auth, async (req, res) => {
const validation = validate(req.body);
if (validation.error) return res.status(400).send(msg400);
try {
const toolInData = await ProgrammingTool.findOne({ name: req.body.name });
if (toolInData) return res.status(400).send(msg400InData);
} catch (e) {
return res.status(500).send(msg500);
}
const tool = new ProgrammingTool({ name: req.body.name });
try {
await tool.validate();
} catch (e) {
console.log("did not pass mongoose validation at posting", e);
return res.status(400).send(msg400);
}
try {
const result = await tool.save();
res.send(result);
} catch (e) {
console.log("failed to post tool");
res.status(500).send(msg500);
}
});
router.delete("/:id", auth, async (req, res) => {
const id = req.params.id;
let tool;
try {
tool = await ProgrammingTool.findById(id);
} catch (e) {
console.log("could not find the tool with provided id", e);
res.status(404).send(msg404);
}
try {
await tool.delete();
} catch (e) {
console.log("failed deleting tool", e);
res.status(500).send(msg500);
}
});
router.put("/:id", auth, async (req, res) => {
const validation = validate(req.body);
if (validation.error) return res.status(400).send(msg400);
const id = req.params.id;
const tool = await ProgrammingTool.findById(id);
if (!tool) res.status(404).send(msg404);
tool.name = req.body.name;
try {
await tool.validate();
} catch (e) {
console.log("did not pass mongoose validation at putting", e);
return res.status(400).send(msg400);
}
try {
const result = await tool.save();
res.send(result);
} catch (e) {
console.log("failed to save edited tool");
res.status(500).send(msg500);
}
});
router.delete("/", auth, (req, res) => {
res.status(400).send(msg400);
});
router.put("/", auth, (req, res) => {
res.status(400).send(msg400);
});
What really suprises me is that the error is getting thrown but route does it job, item is deleted from database.
In the delete route there are 2 problems.
you are not using return so even after the first error it goes to the second try catch block. Add return to both res.status(..).send(..)
For a successful Delete operation you are not returning a 2xx status code. So if there is a successful deletion the request will be stuck cause you are not returning anything, after some time probably it will time out.

How can I throw in the .then of a promise?

I have the following js express code:
app.get('/lists2', (req, res) => {
mongo.getDB()
.then(db => db.collection('dogs'))
.then(collection => collection.find().toArray())
.then(array => res.json(success(array)))
// How can I throw in the middle of a promise to trigger express's middleware?
.catch(error => {
throw {message: "database error"};
});
});
app.use(function (err, req, res, next) {
const message = err.message || 'Encountered a server error';
const status = err.status || 500;
res.status(status).json({status, message});
})
I have written a middleware error handler so I can trigger an API error response with throw the problem is I can't throw inside the then because it's inside async code, is there any method to get around this? Or is my error handling pattern incorrect?
You should use next (cf. doc):
app.get('/lists2', (req, res, next) => {
mongo.getDB()
.then(db => db.collection('dogs'))
.then(collection => collection.find().toArray())
.then(array => res.json(success(array)))
// How can I throw in the middle of a promise to trigger express's middleware?
.catch(error => {
next(new Error("database error"));
});
});
app.use(function (err, req, res, next) {
const message = err.message || 'Encountered a server error';
const status = err.status || 500;
res.status(status).json({status, message});
})

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

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).

Categories