Express.js why does placement of app.use() affect test results? - javascript

I am practicing my knowledge in Express.js .
I have one recipe router with the code below:
const express = require("express");
const router = express.Router();
const Recipe = require("../models/recipe.model");
const createRecipeItem = async recipeData => {
await Recipe.init();
const doc = Recipe(recipeData);
await doc.save();
};
router.post("/", async (req, res, next) => {
try {
await createRecipeItem(req.body);
} catch (err) {
next(err);
}
res.status(201).send(req.body);
});
module.exports = router;
I have another supply router, the code is below:
const express = require("express");
const router = express.Router();
const Supply = require("../models/supply.model");
const createSupplyItem = async supplyData => {
await Supply.init();
const doc = Supply(supplyData);
await doc.save();
};
const updateItem = async (name, itemData) => {
const result = await Supply.findOneAndUpdate({ name }, itemData, {
new: true
});
return result;
};
router.post("/", async (req, res, next) => {
try {
await createSupplyItem(req.body);
} catch (err) {
next(err);
}
const respObj = {};
respObj.name = req.body.name;
respObj.qty = req.body.qty;
res.status(201).send(respObj);
});
router.patch("/:name", async (req, res, next) => {
const updatedItem = await updateItem(req.params.name, req.body);
const response = {};
response.name = updatedItem.name;
response.qty = updatedItem.qty;
res.status(200).send(response);
});
module.exports = router;
I wrote tests for the two routers using supertest
Inside my app.js my code is written like this:
const supplyRouter = require("./routes/supply.route");
const recipeRouter = require("./routes/recipe.route");
app.use("/recipes", recipeRouter);
app.use("/supplies", supplyRouter);
when the code is written like this:
app.use("/recipes", recipeRouter);
app.use("/supplies", supplyRouter);
All my test passed. However, when I change the order of when I call app.use() the test would fail.
app.use("/supplies", supplyRouter);
app.use("/recipes", recipeRouter);
The test would fail with the error Cannot set headers after they are sent to the client for the POST /supplies method. I have no clear understanding why this happens. Appreciate any insight. Thank you!

I cannot pass my test correctly because I didn't have proper understanding of the flow of the code.
router.post("/", async (req, res, next) => {
try {
await createSupplyItem(req.body);
} catch (err) {
next(err);
}
const respObj = {};
respObj.name = req.body.name;
respObj.qty = req.body.qty;
res.status(201).send(respObj);
});
For the above code, when there is an error, express will run two paths of the code namely the path inside the catch block and the last line of the code
res.status(201).send(respObj);
Because it is trying to run two paths, I received the error Cannot set headers after they are sent to the client.
The correct code is written below.
router.post("/", async (req, res, next) => {
try {
await createSupplyItem(req.body);
const respObj = {};
respObj.name = req.body.name;
respObj.qty = req.body.qty;
res.status(201).send(respObj);
} catch (err) {
if (err.name === "ValidationError") {
err.statusCode = 400;
} else if (err.name === "MongoError") {
err.statusCode = 400;
}
next(err);
}
});

Related

Node: Route.get() requires a callback function but got a [object Undefined]

Im trying to create a simple CRUD app in node js and i get Route.get() requires a callback function but got a [object Undefined] on the router.get("/:id", userController.getUser); line
Routes.js
const express = require('express')
const userController= require('../controllers/userController.js')
const router = express.Router()
/* READ */
router.get("/:id", userController.getUser);
module.exports = router
Controller.js
const getUser = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id);
res.status(200).json(user);
} catch (err) {
res.status(404).json({ message: err.message });
}
}
const Function..
const AnotherFunction..
module.exports = {
getUser,
Function,
AnotherFunction,
}
console.log(userController.getUser.toString()) prints:
async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id);
res.status(200).json(user);
} catch (err) {
res.status(404).json({ message: err.message });
}
}
I don't have correct access rights to push into your repo so explaining the issue here.
In your auth.js you have exported verifyToken function as default export.
module.exports = verifyToken.
But you are destructuring when importing in postsController.js
const {verifyToken} = require ('../middleware/auth.js')
Importing like this will get the correct function =>
const verifyToken = require("../middleware/auth.js");
Change your postsController file as below. It should work.
const express = require("express");
const verifyToken = require("../middleware/auth.js");
module.exports = verifyToken;

Cannot read properties node.js

I've got a problem, I wrote a code which should update my user in a db but when I try to do it in Postman I get an information that the programme can't read my property token. Could somebody help me?
My verifytoken.js code
const { response } = require('express');
const jwt = require('jsonwebtoken');
const verifyToken = (req, res, next) => {
const authHeader = res.headers.token;
if(authHeader){
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.JWT_SEC, (err, user) => {
if(err) res.status(403).json("Token isn't valid");
req.user = user;
next();
})
}else{
return res.status(401).json("You are not authenticated");
}
}
const verifytokenandauth = (req, res, next) => {
verifyToken(req, res, ()=>{
if(req.user.id=== req.params.id || req.user.admin) {
next();
}else{
res.status(403).json("You are not allowed to do this");
}
})
}
module.exports = {verifyToken, verifytokenandauth}
My user.js code
const { verifytokenandauth } = require('./verifytoken');
const {User} = require('../models/user');
const router = require('express').Router();
router.put('/:id', verifytokenandauth, async (req, res) => {
if(req.body.password){
req.body.password = CryptoJs.AES.encrypt(req.body.password, process.env.PASS_SEC).toString()
}
try{
const updateduser = await User.findByIdAndUpdate(req.User.id, {
$set: req.body
},{new: true});
res.status(200).json(updateduser);
}catch(err) {res.status(500).json(err);}
});
module.exports = router
And screenshot from Postman
Thanks in advance :)

I have faced CORS policy error .No 'Access-Control-Allow-Origin' header is present on the requested resource [duplicate]

This question already has answers here:
How to enable cors nodejs with express?
(10 answers)
Closed 10 months ago.
I have faced this problem so many time. I require all the middleware in my backend server code but it good for sometimes and then occur, after sometimes it's again running on his own and again cors policy error occur. Please give me a solution . Here is my backend code...
const express = require("express");
const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const port = process.env.PORT || 5000;
require("dotenv").config();
const app = express();
//necessary middleware app.use(cors()); app.use(express.json());
function verifyJWT(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).send({ message: "unauthorized access" });
}
const token = authHeader.split(" ")[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
return res.status(403).send({ message: "Forbidden access" });
}
console.log("decoded", decoded);
req.decoded = decoded;
next();
});
}
const uri = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}#cluster0.nfrv0.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`;
console.log(uri);
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1,
});
async function run() {
try {
await client.connect();
const fruitCollection = client.db("fruitsInventory").collection("fruit");
//AUTH API
app.post("/login", async (req, res) => {
const user = req.body;
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "1d",
});
res.send({ accessToken });
});
//get data
app.get("/inventory", async (req, res) => {
const query = {};
const cursor = fruitCollection.find(query);
const result = await cursor.toArray();
res.send(result);
});
//get inventory id
app.get("/inventory/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await fruitCollection.findOne(query);
res.send(result);
});
//get api with filter email
app.get("/myitem", verifyJWT, async (req, res) => {
const decodeEmail = req.decoded.email;
const email = req.query.email;
if (email === decodeEmail) {
const query = { email: email };
const cursor = fruitCollection.find(query);
const result = await cursor.toArray();
res.send(result);
} else {
res.status(403).send({ message: "Forbidden Access" });
}
});
//delete api
app.delete("/myitem/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await fruitCollection.deleteOne(query);
res.send(result);
});
//post data
app.post("/inventory", async (req, res) => {
const newItem = req.body;
const result = await fruitCollection.insertOne(newItem);
res.send(result);
});
//update data for quantity
app.put("/inventory/:id", async (req, res) => {
const id = req.params.id;
const updateQuantity = req.body;
const filter = { _id: ObjectId(id) };
const options = { upsert: true };
const updateDoc = {
$set: {
quantity: updateQuantity.quantity,
},
};
const result = await fruitCollection.updateOne(
filter,
updateDoc,
options
);
res.send(result);
});
//delete item
app.delete("/inventory/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await fruitCollection.deleteOne(query);
res.send(result);
});
} finally {
}
}
run().catch(console.dir);
app.get("/", (req, res) => {
res.send("fruits server is running");
});
app.listen(port, () => {
console.log("server is connected on port", port);
});
You must set the header of the Axios first because CORS means that you don't have access to origin resource API, so you need to setup the header of Axios first, you can follow this resource CORS
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})

Returning remote API data within Express app

I have an express app where I just return data from another remote API. Below is the file snippet. I cannot use normal node-fetch or request as my remote API uses NTLM auth.
const express = require('express');
const router = express.Router();
const httpntlm = require('httpntlm');
const url = 'http://myremoteapi.com/products';
router.get('/', function(req, res, next) {
httpntlm.get(
{
url,
username: 'my_user',
password: 'my_pass
},
(err, resp) => {
if (err) return err;
res.send(JSON.parse(resp.body));
}
);
});
module.exports = router;
Everything works fine.
Now, I'd like to take the remote API call outside the router method in a function called getData.
const express = require('express');
const router = express.Router();
const httpntlm = require('httpntlm');
const url = 'http://myremoteapi.com/products';
const getData = httpntlm.get(
{
url,
username: 'my_user',
password: 'my_pass
},
(err, resp) => {
if (err) return err;
return JSON.parse(resp.body);
}
);
router.get('/', function(req, res, next) {
res.send(getData) // returns undefined
});
module.exports = router;
I am unable to get the same result. Also, I found that the httpntlm method does not return a Promise which I can resolve.
One way to solve this is to create the Promise yourself:
const express = require('express');
const router = express.Router();
const httpntlm = require('httpntlm');
const url = 'http://myremoteapi.com/products';
const getData = () => new Promise((resolve, reject) => {
httpntlm.get({
url,
username: 'my_user',
password: 'my_pass'
},
(err, resp) => {
if (err) reject(err);
else resolve(JSON.parse(resp.body));
}
);
});
router.get('/', function(req, res, next) {
getData().then(data => res.send(data));
});
module.exports = router;
By wrapping httpntlm.get like that it becomes thenable, and by making getData a function the data is fetched anew whenever you call it.

Why do I get "is not a function" error?

I want to write my test classes for server app. I need to refactor my routers to controller arch.
I return functions on controller's return value. But on my router I see Error. Do you have any idea? Which point am I passing?
Controller:
const express = require('express');
const controller = (Project) => {
const projectMidlleware = require('../middleware/v1/projectMiddleware')(Project)
const put = (req, res, next) => {
projectMidlleware.create(req.query.name, (project) => {
res.status(200).send(project.api_key);
next();
});
}
const findProject = (req, res, next) => {
const apikey = req.headers["s-apikey"] || req.query.apikey || req.body.apikey;
projectMidlleware.findByApikey(apikey, (err, project) => {
if (err) {
next({
message: err.message,
code: 500
});
return;
}
req.body.project = project;
next();
});
}
return {
put,
findProject
}
}
module.exports = controller;
Router:
const express = require("express");
const router = express.Router();
const projectController = require('../../controllers/projectController');
const profileController = require('../../controllers/profileController');
//return profiles for project
router.get("/",
projectController.findProject(),
profileController.get()
);
module.exports = router;
const projectController = require('../../controllers/projectController')();
You have a function that returns an object with methods but you don't call the function and the methods exist on the returned object, not the function itself. This pattern is fairly common in node but I would actually do this for clarity:
const projectController = require('../../controllers/projectController');
const controller = projectController();
and then use that.

Categories

Resources