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.
Related
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;
This is my setting
In the nuxt.config.js
serverMiddleware: ['~/server-middleware/index.js'],
server-middleware/index.js looks like this:
const express = require('express')
const buildRouter = require('../server/router.js')
const app = express()
app.use(express.json())
buildRouter(app)
module.exports = app
server/router.js
module.exports = function (app) {
const buildApiRoute = require('./controllers/api')
buildApiRoute(app, '/api')
}
server/controllers/api.js
module.exports = function (app, path) {
const { body, validationResult } = require('express-validator')
app.get(
path,
body('request2').trim()
.not().isEmpty().withMessage('required')
.escape(),
async function (req, res) {
res.setHeader('Content-Type', 'application/json')
const errors = validationResult(req)
console.log('====================================')
console.log(req.query)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: 'hola' })
}
try {
} catch (e) {
console.log('error', e)
}
}
)
}
The issue i'm facing is that if I access
https://localhost:3000/api/posts?ipp=5&page=5
Its not entering in the middleware but if I enter to
https://localhost:3000/api/?ipp=5&page=5
It does enter and I get the params
How can I get it to "enter" to the middleware and know the slug and the params?
I tried in server/router.js
module.exports = function (app) {
const buildApiRoute = require('./controllers/api')
buildApiRoute(app, '/api/:slug')
}
but not won't enter with /api/users?ipp=5
Also, I don't know how to remove body('request2') without crashing, which I don't need
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);
}
});
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.
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();
}
};