I tried using Async-Await in NodeJs RESTful API but I seem to get an error I cannot resolve.
Here is my db.js:
const User = require('../models/User'),
user = {};
user.findUserByUsername = async function (username) => {
try {
const user = await User.findOne({username});
if (user)
return {data: user, status: 200};
return {message: `Cannot find user with username: ${username}`, status: 404};
} catch (err) {
return err;
}
};
module.exports = user;
And here is my api.js:
const express = require('express'),
router = express.Router(),
user = require('../db/db');
router.get('/user', async (req, res, next) => {
const user = await user.findUserByUsername(req.query.username);
// ^
// Cannot access 'user' before initialization
if (!user.status)
return next(user);
res.status(user.status);
res.doc = user.status === 404 ? user.message : user.data;
next();
});
module.exports = router;
When making a HTTP request, my server is crashing at that point. Where is my mistake?
You're mixing your variable names up. You have user as an import, but you also have user as a variable you're trying to assign to as a result of findUserByUsername.
Use different variable names, and follow the capitalization convention for this sort of database lookup:
const express = require('express'),
router = express.Router(),
User = require('../db/db');
router.get('/user', async (req, res, next) => {
const user = await User.findUserByUsername(req.query.username);
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;
SOLVED
I am currently building an app with Node - Express - MongoDB - ejs.
I have a books router that should render the books ejs file and books list from MongoDB.
Here's the books.js code:
const express = require("express");
const router = express.Router();
const { MongoClient } = require("mongodb");
router.route("/").get(async (req, res, next) => {
const url = "mongodb://localhost:27017";
const dbName = "Library";
let client;
try {
client = await MongoClient.connect(url, { useUnifiedTopology: true });
const db = client.db(dbName);
const books = await db.collection("books").find().toArray();
res.render({
title: "Books",
books,
});
} catch (err) {
res.send(err);
}
});
module.exports = router;
It is supposed to get the data from the MongoDB and send it back to the books page instead I get this error
{ "code": "ERR_INVALID_ARG_TYPE" }
It seems you are not calling your async mongo function. You are only declaring your function. You need to call it by placing () after your declaration like so:
(async function mongo() {
// Your code
})();
So applying that change to your code would result in the following:
const express = require("express");
const router = express.Router();
const { MongoClient } = require("mongodb");
/* GET Books page. */
router.route("/").get((req, res, next) => {
const url = "mongodb://localhost:27017";
const dbName = "Library";
(async function mongo() {
let client;
try {
client = await MongoClient.connect(url, { useUnifiedTopology: true });
const db = client.db(dbName);
const books = await db.collection("books").find().toArray();
res.render({
title: "Books",
books,
});
} catch (err) {
console.log(err);
}
})();
});
module.exports = router;
Because of this your request would indeed timeout as the code within the async function is not called.
Hope this answer was helpful 👍
You define a function called mongo but then you never call it.
Since you never call it, you never call res.render.
Even if you did call it, in the catch branch you never send an error response either.
It looks like you intended it to be an IIFE but forgot the () afterwards. There's no need for that though, you can just make the function you pass as the second argument to get() async instead.
router.route("/").get(async (req, res) => {
const url = "mongodb://localhost:27017";
const dbName = "Library";
try {
const client = await MongoClient.connect(url, { useUnifiedTopology: true });
const db = client.db(dbName);
const books = await db.collection("books").find().toArray();
res.render({
title: "Books",
books,
});
} catch (err) {
console.log(err);
res.status(500).json({error: err})
}
});
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 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.