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.
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
app.js
const express = require('express')
const app = express()
const app = express()
const PORT = process.env.SERVER_PORT || 8080
app.use(express.json())
app.use(express.urlencoded({
extended : true
}))
app.use(formidableMiddleware());
app.listen(PORT, () => {
console.log(`The application is up and running on ${PORT}`)
})
controller.js
This contains the controller that takes base64 encoded image in formdata and that can be accessed with filename property (This is one controller which is working fine with formidable)
const uploadProfilePic = async (req, res) => {
let strArr = req.fields.filename.split(',')
let buffer = new Buffer(strArr[1], 'base64')
let filename =
Date.now().toString() + '' + strArr[0].split('/')[1].split(';')[0]
try {
req.user.profile = buffer
req.user.filename = filename
await req.user.save()
return res.status(200).json(
customMessage(true, {
message: 'Successfully uploaded',
}),
)
} catch (error) {
return res.status(500).status(internalServerError)
}
}
controller2.js This controller is not working properly, it does not even run when we use express-formidable and the post request route to which this controller is binded to, runs forever, but if we pass no request body then it runs perfectly or if we comment out:
//app.use(express-formidable);
//In app.js
then it runs properly but then controller.js doesnt run.
const updateUserData = async (req, res) => {
try {
const {_id, email, name, username, bio, code, platform, languages } = req.body
if (username === undefined || code === undefined || !platform || !languages)
return res
.status(400)
.json(customMessage(false, 'Please Satisy Validations'))
let user = req.user
let user1 = await UserModel.findById(_id)
user1.username = username;
user1.code = code;
user1.bio = bio;
user1.platform = platform;
user1.languages = languages;
if (!user) return res.status(500).json(internalServerError())
else {
await user1.save()
console.log
return res
.status(200)
.json(customMessage(true, `user with ${email} updated`))
}
} catch (error) {
console.log(error)
return res.status(500).json(internalServerError())
}
}
Okay I found a way to send requests through the multipart/form data and the application/json without breaking any thing. I actually spent 2 hours trying to figure out what happened and how to solve the problem. I discovered that the package "express-formidable" is no longer being maintained and has been closed down. Then I also found another package which of course solved the problem just about now "express-formidable-v2" which I believe is the continuation of the former package.
Check this https://github.com/Abderrahman-byte/express-formidable-v2 out, It is a fork of "express-formidable" package
Now you have access to your {req.fields} and {req.body}
I'm using Axios to query an endpoint in my backend. When I try and do this, I get a 404 not found. If I copy/paste the uri it gives in the error from the console and try and access it directly in the browser it connects fine and does not give me an error (instead giving me an empty object which is expected).
Below is my Axios code
axios.post("/api/myEndpoint", { id: this.userID })
.then((response) => {
this.property = response.data.property;
})
.catch((errors) => {
console.log(errors);
router.push("/");
});
Below is the route definition in my backend
const myEndpointRoute = require('../api/myEndpoint.js')();
exprApp.use('/api/myEndpoint', myEndpointRoute);
For reference, the uri is 'http://localhost:3000/api/myEndpoint'. I can access this uri completely fine in the browser but Axios returns a 404 as described above. It is for this reason that I'm confident this is an issue in the frontend, however I have set up this Axios request in the same way as the many others I have and they all work fine.
Edit: here's the rest of the backend
myEndpoint.js
module.exports = function() {
const express = require('express'), router = express.Router();
const authMiddleware = require('../loaders/authMiddleware.js')();
router.get('/', authMiddleware, async function(req, res) {
const id = req.body.id;
const property = await require('../services/myEndpointService.js')
(id).catch((e) => { console.log(e) });
res.send({ property: property });
});
return router;
};
myEndpointService.js
module.exports = async function(id) {
const results = await require('../models/getMyEndpointProperty')(id);
return results;
};
getMyEndpointProperty
module.exports = async function(id) {
const pool = require('../loaders/pool.js')();
const res = await pool.query(`SELECT * FROM myTable WHERE id = ${id};`);
return res.rows;
};
myEndpoint.js defines only a GET method but your axios call sends a POST in the frontend. Try changing (or adding) the express route:
// notice the `.post`
router.post('/', authMiddleware, async function(req, res) {
...
})
It worked when you manually tested it in the browser for this reason as well, since the browser sent a GET request.
I'm kinda new to module creation and was wondering about module.exports and waiting for async functions (like a mongo connect function for example) to complete and exporting the result. The variables get properly defined using async/await in the module, but when trying to log them by requiring the module, they show up as undefined. If someone could point me in the right direction, that'd be great. Here's the code I've got so far:
// module.js
const MongoClient = require('mongodb').MongoClient
const mongo_host = '127.0.0.1'
const mongo_db = 'test'
const mongo_port = '27017';
(async module => {
var client, db
var url = `mongodb://${mongo_host}:${mongo_port}/${mongo_db}`
try {
// Use connect method to connect to the Server
client = await MongoClient.connect(url, {
useNewUrlParser: true
})
db = client.db(mongo_db)
} catch (err) {
console.error(err)
} finally {
// Exporting mongo just to test things
console.log(client) // Just to test things I tried logging the client here and it works. It doesn't show 'undefined' like test.js does when trying to console.log it from there
module.exports = {
client,
db
}
}
})(module)
And here's the js that requires the module
// test.js
const {client} = require('./module')
console.log(client) // Logs 'undefined'
I'm fairly familiar with js and am still actively learning and looking into things like async/await and like features, but yeah... I can't really figure that one out
You have to export synchronously, so its impossible to export client and db directly. However you could export a Promise that resolves to client and db:
module.exports = (async function() {
const client = await MongoClient.connect(url, {
useNewUrlParser: true
});
const db = client.db(mongo_db);
return { client, db };
})();
So then you can import it as:
const {client, db} = await require("yourmodule");
(that has to be in an async function itself)
PS: console.error(err) is not a proper error handler, if you cant handle the error just crash
the solution provided above by #Jonas Wilms is working but requires to call requires in an async function each time we want to reuse the connection. an alternative way is to use a callback function to return the mongoDB client object.
mongo.js:
const MongoClient = require('mongodb').MongoClient;
const uri = "mongodb+srv://<user>:<pwd>#<host and port>?retryWrites=true";
const mongoClient = async function(cb) {
const client = await MongoClient.connect(uri, {
useNewUrlParser: true
});
cb(client);
};
module.exports = {mongoClient}
then we can use mongoClient method in a diffrent file(express route or any other js file).
app.js:
var client;
const mongo = require('path to mongo.js');
mongo.mongoClient((connection) => {
client = connection;
});
//declare express app and listen....
//simple post reuest to store a student..
app.post('/', async (req, res, next) => {
const newStudent = {
name: req.body.name,
description: req.body.description,
studentId: req.body.studetId,
image: req.body.image
};
try
{
await client.db('university').collection('students').insertOne({newStudent});
}
catch(err)
{
console.log(err);
return res.status(500).json({ error: err});
}
return res.status(201).json({ message: 'Student added'});
};
I'm new to next.js so maybe I'm missing something very stupid. I want to use custom routes so I created a server.js file and changed my package.json command to node server.js. This is the entire server.js file:
const express = require("express");
const next = require("next");
const createLocaleMiddleware = require("express-locale");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.get("/", createLocaleMiddleware(), (req, res) => {
res.redirect(`/${req.locale.language}/home`);
});
server.get("/:lang/home", (req, res) => {
const actualPage = "/";
const queryParams = { locale: req.params.lang };
app.render(req, res, actualPage, queryParams);
});
server.listen(3000, err => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
I believe that according to the docs, this should work. I just want to render the index page with the users locale on the specified route ('/:lang/home'). I'm using react-intl for the i18n.
Now I get the following error in the console (client side):
It's in dutch but it's just saying it can't find any of the specified files. So now the HMR is not working anymore, routing is not working anymore (with Router.push). The only thing it does correctly is loading the index page (I can see it in the browser).
I also tried to enable and disable this flag from the docs:
module.exports = {
useFileSystemPublicRoutes: false
}
Sadly, no effect.
Am I missing something? Is it because I'm redirecting? Or is this not to way to handle routing? If someone could provide some pointers that would be great :)
Thanks in advance!
You are missing server.get('*', handle) as you can see in the custom server express example. This is absolutely required :)