I am new in Mern development, and getting this "Cannot access 'user' before initialization" error in controller file
Controller/user.js
const user = require('../models/user')
exports.signup = (req,res)=>{
console.log('req.body', req.body);
const user = new user(req.body);
user.save((err, user)=>{
if(err){
return res.status(400).json({
err
})
}
res.json({
user
})
})
}
Route/user.js
const express = require('express');
const router = express.Router();
const {signup} = require('../controllers/user');
router.post("/signup", signup);
module.exports = router;
On line 1 you define a variable named user.
On line 4 you define a different variable also named user in a different scope.
At the other end of line 4 you try to call the value of that variable as a function, but it doesn't have a value yet. Hence the error message.
You intended to call the function in the variable from line 1, but since the one on line 4 shadows it, you are calling that instead.
Don't shadow your variables.
Change the name of one of them.
Related
I have been following a tutorial on setting up REST APIs in Node, using Express for an app that accesses an existing MariaDB database. My version only needs to read data and I have the DB co-located with the Node application (same host).
My goal for this entry-level example is to just access the data, using static SQL, so I can see it rendered in the web page by the JSON pritifier.
[Next, I want to present the data in a table (EJS?). Later, when I can get that to work, I'll add form controls (React?) to let a user specify start and end date bounds for the SQL query. Finally I'll aim to render the data as a line graph (D3js).]
The tutorial runs the web server successfully (it returns 'OK' on the base URL), but when I go to URL/solarData it tries an async function to getMultiple rows from the DB, it responds:
Bind parameters must not contain undefined. To pass SQL NULL specify JS null TypeError: Bind parameters must not contain undefined. To pass SQL NULL specify JS null
at /SunnyData/solarViz/node_modules/mysql2/lib/connection.js:628:17
at Array.forEach (<anonymous>)
at Connection.execute (/SunnyData/solarViz/node_modules/mysql2/lib/connection.js:620:22)
at /SunnyData/solarViz/node_modules/mysql2/promise.js:120:11
at new Promise (<anonymous>)
at PromiseConnection.execute (/SunnyData/solarViz/node_modules/mysql2/promise.js:117:12)
at Object.query (/SunnyData/solarViz/services/db.js:6:40)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Object.getMultiple (/SunnyData/solarViz/services/solarData.js:7:16)
at async /SunnyData/solarViz/routes/solarData.js:8:14
app.js:61
./app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3800;
const solarDataRouter = require('./routes/solarData');
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
app.get('/', (req, res) => {
res.json({'message': 'ok'});
})
app.use('/solarData', solarDataRouter);
/* Error handler middleware */
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
console.error(err.message, err.stack);
res.status(statusCode).json({'message': err.message});
return;
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
./routes/solarData.js
const express = require('express');
const router = express.Router();
const solarData = require('../services/solarData');
/* GET solar data. */
router.get('/', async function(req, res, next) {
try {
res.json(await solarData.getMultiple(req.query.page));
} catch (err) {
console.error(`Error while getting solar data `, err.message);
next(err);
}
});
module.exports = router;
./config.js
const env = process.env;
const config = {
db: {
host: env.SUNNY_HOST,
user: env.SUNNY_USER,
password: env.SUNNY_PW,
database: env.SUNNY_DB,
},
listPerPage: env.LIST_PER_PAGE,
};
module.exports = config;
./services/solarData.js
const db = require('./db');
const helper = require('../helper');
const config = require('../config');
async function getMultiple(page = 1){
const offset = helper.getOffset(page, config.listPerPage);
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
const data = helper.emptyOrRows(rows);
const meta = {page};
return {
data,
meta
}
}
module.exports.getMultiple = getMultiple;
./services/db.js
const mysql = require('mysql2/promise');
const config = require('../config');
async function query(sql, params) {
const connection = await mysql.createConnection(config.db);
const [results, ] = await connection.execute(sql, params);
return results;
}
module.exports = {
query
}
I've left out the ./helper.js
Everything runs fine until I direct the webpage to /solarData. At that point I get the Debug Console (vscode) mentioned up-front
Searching seems to point at a mysql2 shortcoming/bug but not at a practical solution
If you respond, please describe the 'bind' mechanism, as I'm not sure what's going on.
Hope I've put enough info in. Please ask if I need to add anything else.
The error says
Bind parameters must not contain undefined.
It means that in the file ./services/solarData.js on the line
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
Some of the 2 variables is undefined, you need to check offset and config.listPerPage to be defined.
Just use
console.log('offset: ' + offset)
console.log('listPerPage: ' + config.listPerPage)
and you will find out what is undefined in your case
I'm reading a books about node.js, express and mongodb. The author uses connect-flash. However, I can't seem to get it to work correctly. The folders in which connect-flash is used is shown below with the irrelevant code removed.
index.js
const flash = require('connect-flash');
app.use(flash());
const newUserController = require('./controllers/newUser')
const storeUserController = require('./controllers/storeUser')
app.get('/auth/register', redirectIfAuthenticatedMiddleware, newUserController)
app.post('/users/register', redirectIfAuthenticatedMiddleware, storeUserController)
controllers/storeUser.js
const User = require('../models/User.js')
const path = require('path')
module.exports = (req,res)=>{
User.create(req.body, (error, user) => {
if(error){
const validationErrors = Object.keys(error.errors).map(key => error.errors[key].message)
// req.session.validationErrors = validationErrors
req.flash('validationErrors',validationErrors)
return res.redirect('/auth/register');
}
res.redirect('/')
})
}
Controllers/newUser.js:
module.exports = (req, res) =>{
res.render('register',{
errors: flash('validationErrors')
})
}
The error
ReferenceError: flash is not defined.
This error occurs at controllers\newUser.js:3:17)
I have reread the chapter many times and searched for a solution without luck. I don't understand why it's undefined when it's declared in index.js.
Why is flash undefined? and how do I fix it?
module.exports = (req, res) =>{
res.render('register',{
errors: req.flash('validationErrors')
})
}
You may be missing calling it from req.
So I just watched some tutorials on Youtube it's about uploading a file with Multer.
I need to export the variable gfs when it's connected to my /routes folder, but how can I do that?
const mongoose = require("mongoose")
const Grid = require("gridfs-stream")
// Routes
const user = require("./routes/api/users")
// Init gfs
let gfs
// create gfs on connection 'open'
mongoose.connection
.once("open", () => {
// Init stream
gfs = Grid(mongoose.connection.db, mongoose.mongo)
gfs.collection("uploads")
console.log("mongoDB connected and gfs has been created")
})
.on("error", err => {
console.log("connection error", err)
})
// I need the gfs on the user routes
// use routes
app.use("/api/users", user)
If you'd like to access gfs inside your middleware and/or controller, you could do the following:
mongoose.connection
.once('open', () => {
const gfs = Grid(mongoose.connection.db, mongoose.mongo)
gfs.collection('uploads')
// use `app.locals` to store the result when it finishes
app.locals.gfs = gfs
})
According to the Express documentation:
The app.locals object has properties that are local variables within the application.
Once set, the value of app.locals properties persist throughout the life of the application, in contrast with res.locals properties that are valid only for the lifetime of the request.
It's important to remember, however, that inside of your middleware and/or controller, where your routes are being handled, you need to check if gfs exists before you do something with it.
The above call is asynchronous, so gfs won't be immediately available for you to use:
app.use('api/users/', user)
// routes/api/users.js
route.get('/', function(req, res, next) {
if (req.app.locals.gfs) {
// check if `app.locals.gfs` exists
}
// handle a case where `app.locals.gfs` hasn't been set yet
})
If you'd like to learn more about app.locals, here's a link:
app.locals
let conn = mongoose.createConnection(<url>);
let gfs = Grid(conn.db, conn.mongo);
gfs.collection("uploads");
// write routes
module.exports = gfs;
I have an Express application with a router, here is the example of the router:
const router = require('express-promise-router')();
const users = require('./users');
const validate = require('./validate');
router.get('/users', users.list);
router.get('/users/:id', users.get);
// other routes here
module.exports = router;
Now I want to add a middleware that validates each query, like that (this is not the working example, it's just to show the idea of what I want to accomplish):
const schemas = {
'/users': 'some validation schema',
'/users/:id': 'another validation'
}
module.exports = (req, res, next) => {
const url = req.originalUrl; // This is where I'm stuck.
if (!schemas[url]) {
// throw new error that validation failed
}
// validating somehow
if (!req.validate(schemas[url])) {
// throw new error that validation failed
}
return next();
}
And for this, I need to get the middlelware mount folder (like '/users/:id' for '/users/557'). I've tried to use req.originalUrl, but it returns the full URL path instead of the mount folder.
How can I achieve this? And if there's no way, how can I write my validation middleware another way to make it work?
Inside req.route you will get the path of API.
Check this screenshot
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
(req, email, password, done) => {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(() => {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email },function(err, user){
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, {'errorMessages': 'That email is already taken.'});
} else {
// if there is no user with that email
// create the user
let newUser = new User();
// set the user's local credentials
newUser.name = req.body.fullname;
//newUser.email = email;
newUser.password = newUser.generateHash(password);
// save the user
newUser.save((err)=>{
if (err)
return done(err);
return done(null, newUser);
});
}
});
});
}));
The above code is in node js using passport js authentication and the code of local-signup is not working.
In the above code i am getting the error:
User.findOne() is not a function.
My schema is all right... please help
You need to (if you're not already) create instance of your data with a model like
var UserDetails = mongoose.model('userInfo', UserDetail);
Now you should be able to use .findOne here.
And make sure you're defining structure for your date inside a collection like..
var Schema = mongoose.Schema;
var UserDetail = new Schema({
username: String,
password: String
}, {
collection: 'userInfo'
});
Kindly, use the code below
module.exports = User = mongoose.model('user', UserSchema)
User should be the model name and remember to define const UserSchema = new Schema at the top to create a new model in MongoDB and
user should the route where you have the
router.post('/user') (req, res) => { code here }
with this, you are exporting the mongoose schema to the route user, this which enables findOne to be seen as a mongoose function.
maybe you did not export the Model from your User model folder.
eg:
module.exports = User = mongoose.model("users", UserSchema);
Export the user Model from models directory from file named user.js.
module.exports.User = User
Then Load User model from any other
const {User} = require('../models/user.js');
Note : I'm assuming user models file is named user.js
User.findOne is not a function error
In my case:
CORRECT SYNTAX:
const User = require('../../models/User') // rectified
Error occured due to VS code auto-format my import changed to below
const User = require - '../../models/User'
:- Though this import is the culprit, VS code still validated this import !
You can try this:
user.collection.findOne(...)
use
module.exports = User = mongoose.model("users", UserSchema);
instead of
module.exports = User => mongoose.model("users", UserSchema);
In my case the problem was that instead of writing the below code
module.exports = UserModel;
I had wrote
exports.module = UserModel;
It is how you export and import your Model. If you are defining model as const User = mongoose.model("users", UserSchema); then exporting it by export default User;. you should use the following to import that:
import User from '../db/models/user';
instead of
const User = require('../db/models/user');
TRY THIS
make sure you imported your schama as import User from '../db/models/user'; instead of const User = require('../db/models/user'); if you're using es6 syntax
If your error contains also something like Warning: Accessing non-existent property '...' of module exports inside circular dependency and TypeError: User.findOne is not a function, and generaly the model is an empty {}, then the problem may be the connection (import and export) of the both files. The file that exports the used model can most likely also import the destionation file where the model is used.
Example: Let us have 2 files, user - exporting the User model, and the receiver - example.js
user.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const exmple = require("./example"); //notice how the destination file is also imported in the sender model file
//... all your code and schema here
const User = mongoose.model('User', UserSchema);
module.exports = User;
example.js
const User = require("./user"); // circular dependency
//... your code
This mostly happens, as in my case, when having 2 model files, and importing, and exporting each one into another creating the error. I needed the export model for some User model functions.
I also had this situation where:
const User = require('../../mongoose/User');
User.findOne(...);
returned 'findOne is not a function'
But by changing to:
var User = require('../../mongoose/User');
User.findOne(...);
..it worked.
Hope I helped someone !
In my case I defined User like this ↓
const User = '../../models/User'
instead of ↓
const User = require('../../models/User')