app.get("/editMBTI", editMBTIFunc(req, res)
{
// making MongoClient available to all the EJS Files
// app.locals.MongoClient= MongoClient;
MongoClient.connect(url, function (err, client) {
assert.equal(null, err);
console.log("Connected Successfully to the Database server");
const db = client.db(dbName);
//getting the whole collection of MBTI sets
var cursor = db.collection("mbti_testcontent").find();
cursor.each(function (err, doc) {
console.log(doc);
//send the above retrieved doc to the editMBTI.ejs file(front- end)
res.render('editMBTI', {
'mbti_content': doc,
'db_url': url,
'dbName': dbName
});
});
});
});
The above is the code and the image of the terminal(https://i.stack.imgur.com/XcOti.png). Why is the missing argument bracket error poping up in the editMBTI api ? I have closed all the brackets that were opened. Where is it missing ?
Change this line:
app.get("/editMBTI", editMBTIFunc(req, res)
to this:
app.get("/editMBTI", function editMBTIFunc(req, res)
FYI, a tool like JSHint or JSLint will often give you more detailed info about where something is wrong (which is what I used to see this more easily).
Related
I have a global variable in my server side code inside a post route and i am trying to access it within another post route. The code executes perfectly when the second post route is called for the first time, but when the second post route is triggered a second time, the value of the global variable becomes undefined.
Snippet of first post route that declares the global variable:
app.post("/login", function(req, res) {
...
fs.readFile('results.json', function (err, data) {
if(err){
console.log(err)
} else{
var json = JSON.parse(data)
...
global.Identifier;
global.Identifier = Identifier;
return global.Identifier;
}
}
);
res.redirect("/");
});
snippet of second post request that accesses global variable:
app.post("/addtask", function(req, res) {
var gloablIdentifier = global.Identifier;
...
res.redirect("/");
};
(When the second post request is accessed a second time, the value of gloablIdentifier = undefined )
NOTE: I understand that using global variables is VERY BAD PRACTICE and should be avoided in most situations at all costs, but I would still like to work on this problem.
Thanks in advance
You did not wait readFile has to be finished and return response. Since, fs.readFile is async, global.Identifier will update later. You can await to read the file and then return;
app.post("/login", function(req, res) {
fs.readFile("results.json", function(err, data) {
if (err) {
console.log(err);
} else {
var json = JSON.parse(data);
global.Identifier = Identifier;
return global.Identifier;
}
// wait for read file
res.redirect("/");
});
// res.redirect("/");
});
async-await version:
const { promisify } = require("util");
const readFile = promisify(fs.readFile);
app.post("/login", async function(req, res) {
try {
const data = await readFile("results.json");
var json = JSON.parse(data);
global.Identifier = Identifier;
return res.redirect("/");
} catch (error) {
return res.redirect("/");
}
});
From the looks of it, you are using something like Express.
Even though this might be a bad practice I think that you should give a try to using the set functionality provided by express itself.
As such, in your app.js do it like this:
app.set("identifier", Identifier)
Then, in your routes:
app.post("/addtask", function(req, res) {
var gloablIdentifier = app.get('identifier')
...
res.redirect("/");
};
I did not test this, it's based on the documentation and on this answer
EDIT: I tested this and it works. Also, to change the variable again just do this on your route:
app.set('identifier', newValue)
I hope the answer is complete now!
I'm trying to save a variable to a text file, but if the variable isn't found when using spotifyApi.clientCredentialsGrant(), then I want my server to redirect to app.get('/error', function(req, res) {}); which displays a different webpage, but it's returning the error:
(node:11484) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
How can I get around this error to display the webpage error.html?
I don't have access to EJS or window.location because it conflicts with other files and it's a node.js program, respectively.
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
try {
spotifyApi.clientCredentialsGrant()
.then(function (data) {
// Save the access token so that it's used in future calls
client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
}, function (err) {
console.log('Something went wrong when retrieving an access token', err.message);
throw err;
});
fs.writeFile("./public/client_cred_token.txt", '', function (err) {
console.log('Clearing previous access token');
});
fs.writeFile("./public/client_cred_token.txt", client_cred_access_token, function (err) {
if (err) return console.log(err);
});
fs.readFile('./public/client_cred_token.txt', function (err, data) {
if (err) throw err;
console.log("Saved Client Credentials as: %s", data)
});
}
catch (err) {
res.redirect('/error');
}
});
Key takeaway from the accepted answer is to not send any HTML/files to the server until it's confirmed which one is needed.
You are calling res.sendFile() first and then if you later get an error, you are also calling res.redirect('/error') which means you'll be trying to send two responses to one http request which triggers the error you see. You can't do that.
The solution is to call res.sendFile() at the end of all your other operations so you can then call it when successful and call res.redirect() when there's an error and thus only call one or the other.
In a difference from the other answer here, I've shown you how to code this properly using asynchronous file I/O so the design could be used in a real server designed to serve the needs of more than one user.
const fsp = require('fs').promises;
app.get('/', async function (req, res) {
try {
let data = await spotifyApi.clientCredentialsGrant();
// Save the access token so that it's used in future calls
client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
await fsp.writeFile("./public/client_cred_token.txt", client_cred_access_token);
let writtenData = await fsp.readFile('./public/client_cred_token.txt');
console.log("Saved Client Credentials as: %s", writtenData);
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
} catch (err) {
console.log(err);
res.redirect('/error');
}
});
app.get('/', function (req, res) {
try {
spotifyApi.clientCredentialsGrant().then(function (data) {
// Save the access token so that it's used in future calls
let client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
// truncate token file
fs.truncateSync("./public/client_cred_token.txt");
// write token to file
fs.writeFileSync("./public/client_cred_token.txt", client_cred_access_token);
// read token from file again
// NOTE: you could use `client_cred_access_token` here
let data = fs.readFileSync('./public/client_cred_token.txt');
console.log("Saved Client Credentials as: %s", data)
// send homepage to client when no error is thrown
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
}, function (err) {
console.log('Something went wrong when retrieving an access token', err.message);
throw err;
});
} catch (err) {
res.redirect('/error');
}
});
I swapped all asynchron file opreations with the syncron one.
They throw an error and you dont have to deal with callback chain/flow.
Also i moved the sendFile(...) at the botom in the try block, so when a error is thrown from any syncrhonus function call the sendFile is not reached, and your redirect can be sent to the client.
Otherwise you would send the homepage.html to the client, with all headers, and a redirect is not possible.
For my project I'm trying to create an audio player. The database aspect of storing files is new to me as I've only stored strings before.
So far, what I've been able to do is:
Store the audio file into the database.(I'm linking to a file here for simplicity but in the future it will be uploaded)
Retrieve the audio file as an object.
Store the audio file in the public folder for use.
Server side code(the route code is separate from the server code)
let fs = require('fs');
var bodyParser = require('body-parser')
var urlencodedParser = bodyParser.urlencoded({
extended: false
})
const MongoClient = require('mongodb').MongoClient;
const Binary = require('mongodb').Binary;
const ObjectId = require('mongodb').ObjectId;
module.exports = function(app) {
app.get('/music', function(req, res) {
//STEP ONE
var data = fs.readFileSync(__dirname + '/../public/recordings/Piso 21 - Puntos Suspensivos.mp3');
var insert_data = {};
insert_data.name = 'Piso 21 - Puntos Suspensivos.mp3';
insert_data.file_data = Binary(data);
MongoClient.connect("mongodb://localhost/songs", {
useNewUrlParser: true
}, function(err, db) {
if (err) throw err;
var dbo = db.db("songs");
dbo.collection("song").insertOne(insert_data, function(err, res) {
if (err) throw err;
console.log("1 document inserted");
db.close();
});
});
//STEP TWO
MongoClient.connect("mongodb://localhost/songs", {
useNewUrlParser: true
}, function(err, db) {
if (err) throw err;
var dbo = db.db("songs");
dbo.collection("song").findOne({
name: 'Piso 21 - Puntos Suspensivos.mp3'
}, function(err, result) {
if (err) throw err;
db.close();
//STEP THREE
fs.writeFile(result.name, result.file_data.buffer, function(err) {
if (err) throw err;
console.log(result);
});
});
});
res.render('audio');
});
The third step is what I don't what to do. I'd like to send the result object to the audio.ejs page and somehow give the audio tag access to it without having to save it in the public folder an then have to delete it after use.
Something like this,
STEP THREE
res.render('audio', result);
and somehow give an audio tag access to it in the audio.ejs page
UPDATE
let fs = require('fs');
var bodyParser = require('body-parser')
var urlencodedParser = bodyParser.urlencoded({ extended: false })
const MongoClient = require('mongodb');
const Binary = require('mongodb').Binary;
const ObjectId = require('mongodb').ObjectId;
const Grid = require('gridfs-stream');
const db = new MongoClient.Db('songs', new MongoClient.Server("localhost", 27017));
const gfs = Grid(db, MongoClient);
const bcrypt = require('bcryptjs');
module.exports = function(app){
app.get('/audio/:filename', function (req, res) {
MongoClient.connect("mongodb://localhost/songs", { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("songs");
dbo.collection("song").findOne({name: req.params.filename}, function(err, result){
if (err) throw err;
db.close();
const readstream = gfs.createReadStream(result.file_data);
readstream.on('error', function (error) {
res.sendStatus(500);
});
console.log(res);
res.type('audio/mpeg');
readstream.pipe(res);
});
});
});
In old-timey database lingo media objects are called BLOBS -- binary large objects. In Mongo they're handled with a subsystem known as gridfs. There's a nice npm module called gridfs-stream to make this easier.
An easy way to deliver media objects to browsers is to make them available behind URLs that look like https://example.com/audio/objectname.mp3. And, they should be delivered with the appropriate Content-Type header for the codec in use (audio/mpeg for MP3). Then the src tag can simply name the URL and you're rockin' and rollin'. The audio tag in the browser page looks something like this:
<audio controls src="/audio/objectname.mp3" ></audio>
So, if you want to deliver audio directly via express, you need a route with a parameter, something like
app.get('/audio/:filename', ...
Then the node program uses something like this not debugged!)
const mongo = require('mongodb');
const Grid = require('gridfs-stream');
...
const db = new mongo.Db('yourDatabaseName', new mongo.Server("host", 27017));
const gfs = Grid(db, mongo);
...
app.get('/audio/:filename', function (req, res) {
const readstream = gfs.createReadStream({filename: req.params.filename})
readstream.on('error', function (error) {
res.sendStatus(500)
})
res.type('audio/mpeg')
readstream.pipe(res)
});
This is cool because streams are cool: your node program doesn't need to slurp the whole audio file into RAM. Audio files can be large.
gridfs offers the mongofiles command line utility for loading files into gridfs.
But, all that being said: Most scalable media services use static media files delivered from file systems and/or content delivery networks. Servers like apache and nginx have many programmer years invested in making file delivery fast and efficient. The database holds the pathnames for the files in the CDN.
How to troubleshoot this kind of thing?
Watch the browser's console log.
Hit the media URL directly from a browser. See what you get. If it's empty, something's wrong with your retrieval code.
In dev tools in the browser, look at the Network tab (in Google Chrome). Look for the media object, and examine what's going on.
I think what you're looking for is a stream, so you can stream data from the server to the webpage directly without saving it. Node js comes with this functionality more of it from the documentation here https://nodejs.org/api/stream.html
So I just started learning MEAN and I want to show only a certain field of a database I've made to a Node server. I'm using Express as well. Here is my code so far.
index.js
router.get('/generate', function(req, res) {
// get out mongoclient to work with our mongo server
var MongoClient = mongodb.MongoClient;
// where the mongodb server is
var url = 'mongodb://localhost:27017/data';
MongoClient.connect(url, function(err, db) {
if(err) {
console.log('Unable to connect to server', err);
} else {
console.log('Connection established');
var collection = db.collection('compliments');
collection.find({}).toArray(function(err, result) {
if (err) {
res.send(err);
} else if (result.length) {
res.json(result); // problem here
} else {
res.send('No documents found');
}
db.close();
});
}
});
});
generate.jade
doctype html
html
head
title("Compliment Generator")
body
h1 !{title}
block content
h3.
!{compliment}
This is what it looks like on localhost:3000/generate
[{"_id":"570b50f8265f2536d2fd6ed6","type":"compliment","content":"You are absolutely gorgeous."},{"_id":"570b50f8265f2536d2fd6ed7","type":"compliment","content":"You are wonderful."},{"_id":"570b50f8265f2536d2fd6ed8","type":"compliment","content":"I could look at you all day."}]
How do I make it so that it only displays the "content"? Thanks!
If I understand correctly you only want the content to be returned from the query.
The below link should be of use:
https://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/
You essentially want to modify the query to only retrieve the "Content" part like so:
collection.find({}, { content: 1, _id:0 })
This will specify that you don't want to include the "_id" (which is included by default) and you do want to include the "content".
I'm wondering how to use the module node-mysql correctly in Node.js (using Express.js). I have a main router with this:
var Post = require('./models/post.js');
app.get('/archives', function (req, res) {
Post.findArchives(function(posts, err) {
if(err)
res.send('404 Not found', 404);
else
res.render('archives', { posts: posts});
});
});
And here's the content of the file post.js:
var mysql = require('mysql');
var dbURL = 'mysql://root#localhost/mydatabase';
exports.findArchives = function(callback) {
var connection = mysql.createConnection(dbURL);
connection.query('SELECT * FROM blog_posts_view WHERE status != 0 ORDER BY date DESC', function(err, rows) {
if(err) throw err
callback(rows, err);
connection.end();
});
};
How can I improve it? Improve the error handling? Also, there's the function handleDisconnect(connection); on their Github (https://github.com/felixge/node-mysql) that I'm not really sure how to integrate to make sure that the application will not crash when the database is not responding.
Thanks!
Take a look at the mysql-simple library. It combines node-mysql with a pooling library to create a connection pool, and also includes the code to handle the disconnects.
If you want to make it super easy, you could just use that module.