Node JS async/await controller issue - javascript

I am trying to use an asynchronous function in my Node API controller, but am receiving an error from my 'error-handler' middleware.
TypeError: fn is not a function
at eval (webpack:///./app/middleware/errorHandler.js?:16:21)
It does not like my 'findAll' function exported from my controller, why is this not a function? Am I exporting the function correctly? Am I using async/await correctly? Do I need a polyfill for this? I understood that async/await was supported from Node v8. I am currently running Node v11.10 and Express v4.16.4.
Here is my routes file:
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const errorHandler = require('../../middleware/errorHandler.js');
module.exports = app => {
const controller = require('../../controllers/controller.js');
app.get(`/collection`, verifyToken, errorHandler(controller.findAll));
}
Here is my controller:
// controller.js
exports.findAll = async (req, res) => {
const collections = await collection.find().populate('other');
res.send(collections);
};
Here is my middleware:
// errorHandler.js
module.exports = fn => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
Any help is greatly appreciated.

Im not sure but is errorHandler expecting fn to be the error? If so, why is it called passing (req, res next)?
I use following structure:
Router
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const controller = require('../../controllers/controller.js');
var router = express.Router()
router.route('/collection').get(
verifyToken,
controller.findAll
)
module.exports = router
Controller
// controller.js
const asyncUtil = fn =>
function asyncUtilWrap(req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
module.exports = {
findAll: asyncUtil(async (req, res, next) => {
const collections = await collection.find().populate('other'); // you can do try/catch here if you want to handle the error here
res.send(collections);
};
Then Error Handler usually goes at bottom of app.js (but you can place it at bottom of your router):
// app.js
app.use(function(err, req, res, next) {
res.status(err.status || 500)
res.send(err.message)
})

I believe this is how I would do it if I understand you correctly:
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const controller = require('../../controllers/controller.js');
module.exports = app => {
app.get(`/collection`, verifyToken, controller.findAll);
}
// controller.js
exports.findAll = async (req, res, next) => {
try {
const collections = await collection.find().populate('other');
res.send(collections);
} catch(err) {
console.log(err); // up to you what to do with the error
next();
}
};

Related

Express Middlewares execute their arguments, while JavaScript functions doesn't

I want to write an express middleware, I'm calling it express-stack-player.
What this middleware does is that it gives your middlewares access to the objects (req, res, next), by getting the benefit of the JavaScript scopes ex:
The following is a very simplified version of its code, which sums up its core job of it:
const stackPlayer = (stacks) => (req, res, next) => {
// #param stacks: function
const stacksValue = stacks(req, res, next);
for (middleware of stacksValue) {
middleware(req, res, next);
}
};
It used to work during my project which I'm working on until eventually, I discovered an unwanted behaviour by running the following test:
const express = require("express");
const app = express();
const setter = (req, res, next) => {
console.log("hello world");
req.a = {};
req.a.b = 1;
next();
};
const readSomething = (something) => (req, res, next) => {
console.log(something);
};
const stackPlayer = (stacks) => (req, res, next) => {
// #param stacks: function
const stacksValue = stacks(req, res, next);
for (middleware of stacksValue) {
middleware(req, res, next);
}
};
app.get(
"/",
stackPlayer((req, res, next) => [setter, readSomething(req.a.b)])
);
console.clear();
app.listen(4000, () => console.log("listening on port 4000"));
When I run npm start, it prints the following in the terminal just normally:
But when I request GET /, the following error prints out:
I was testing this JavaScript code in the browser to see its behavior:
const obj = {}
const setOnObj = () => {
obj.a = {}, obj.a.b = 1
}
const readSomething = (arg) => {
console.log(arg)
}
setOnObj()
readSomething(obj.a.b)
If you click on Run code snippet, the output will be 1. So why does my express-stack-player package have a different behavior?
By the way, the package is on npm, and it can be downloaded by running
npm i #samislam/switcher
In the past, I made a bigger package called switcher, but now I want to move the core logic of executing the middlewares into a separate package, and I'll call it express-stack-player (if the name would be available by then).

GET method not working on specific route in ExpressJS

When I try to get data using /:user the function is not running inside the specific routing, can anyone figure me out what is the mistake here?
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const {accountSC,catalogSC} = require("./schema");
let dburl = **database url**
app.use(express.json());
mongoose.connect(dburl);
app.get("/catalog", async (req, res) => {
//some func inside it
});
app.get("/:catalog/:id", async (req, res) => {
//some func inside it
});
app.get("/:user", async (req, res) => {
//some func inside it
});
Basically your /catalog and /:user are the same because of the structure. :user value can be catalog also. So, try out different naming approaches of the routes (/info/:user).
Thank you.
update:
Try out this one.
app.get("/catalog", async (req, res) => {
//some func inside it
});
app.get("/catalog/:catalog/:id", async (req, res) => {
//some func inside it
});
app.get("/info/:user", async (req, res) => {
//some func inside it
});

TypeError: require(...).registerUser is not a function

I am trying to import registerUser function inside router.post, in the same file in which i have exported its function (registerUser) so that it can be used outside this module also but it says
TypeError: require(...).registerUser is not a function
please have a look -
**user.js // express route for 'api/user/signup/ --------**
const router = require('express').Router();
const userModel = require('../models/user');
exports.registerUser = async (req, res, next) => {
console.log('in registeruser')
}
router.post("/signup", (req, res, next) => {
require('./user').registerUser(req, res, next);
console.log('in signup')
next();
})
module.exports = router;
You shouldn't require the same file you're already in.
Instead just create the function, and then later on export it. Your code would then look like this.
const router = require('express').Router();
const userModel = require('../models/user');
async function registerUser(req, res, next) {
console.log('in registeruser')
}
router.post("/signup", (req, res, next) => {
registerUser(req, res, next);
console.log('in signup')
next();
})
exports.registerUser = registerUser;
module.exports = router;
Not sure why you are trying to reimport it, just declare it as a variable and use it directly, you can still export it
const router = require('express').Router();
const userModel = require('../models/user');
const registerUser = async (req, res, next) => {
console.log('in registeruser')
next();
}
// since registerUser is also middleware, you can pass it as a param to post
router.post("/signup", registerUser, (req, res, next) => {
console.log('in signup')
next();
});
exports.registerUser = registerUser;
If you want to use the registerUser in the same file, you don't need to export it.
const router = require('express').Router();
const userModel = require('../models/user');
registerUser = async (req, res, next) => {
console.log('in registeruser')
}
router.post("/signup", (req, res, next) => {
registerUser(req, res, next);
console.log('in signup')
next();
})
module.exports = router;
If you want to use the registerUser in other modules, simply export it by modifying your exports statement
module.exports = {router, registerUser}

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

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

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