I am using mlab and mongojs along with Angular. When I attempt to toggle a boolean value (onStatus), the button in the view toggles from off to on, but it crashes the app. When I check in the DB the property has been removed from the document. Code snippets below:
device.service.ts
toggleDevice(updatedStatus){
var headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.put('/api/device/'+updatedStatus._id, JSON.stringify(updatedStatus), {headers: headers})
.map(res => res.json());
}
devices.component.ts
toggleDevice(device){
var currentStatus = device.onStatus;
var updatedStatus = {
_id: device._id,
name: device.name,
onStatus: !currentStatus
};
this.deviceService.toggleDevice(updatedStatus)
.subscribe(data => {
device.onStatus = !device.onStatus
});
}
devices.compoonents.html
<button class="btn" (click)="toggleDevice(device)" type="button">{{ device.onStatus ? 'Switch Off' : 'Switch On' }}</button>
API routing
var express = require('express');
var router = express.Router();
var database = require('../config/database');
var mongojs = require('mongojs');
var db = mongojs(database.url, ['devices'])
// GET : All devices
router.get('/devices', function(req, res, next){
db.devices.find(function(err, devices){
if(err) {
res.send(err);
}
res.json(devices);
});
});
// GET : Single device
router.get('/device/:id', function(req, res, next){
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
// POST : Save a device
router.post('/device', function(req, res, next){
var device = req.body;
device.onStatus = false;
if(!device.name) {
res.status(400);
res.json({
"error": "Please add a name."
});
} else {
db.devices.save(device, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
}
});
// DELETE : A device
router.delete('/device/:id', function(req, res, next){
db.devices.remove({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
// PUT : Update a device
router.put('/device/:id', function(req, res, next){
var device = req.body;
var updatedDevice = {};
if(device.name) {
updatedDevice.name = device.name;
}
if(!updatedDevice){
res.status(400);
res.json({'Error': 'Name not specified'});
} else {
db.devices.update({_id: mongojs.ObjectId(req.params.id)}, updatedDevice, {}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
}
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
module.exports = router;
Errors
/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
at ServerResponse.header (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:719:10)
at ServerResponse.send (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:164:12)
at ServerResponse.json (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:250:15)
at /home/chopin/Development/homeautomation/routes/devices.js:80:9
at /home/chopin/Development/homeautomation/node_modules/mongojs/lib/collection.js:50:5
at handleCallback (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/utils.js:95:56)
at /home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/cursor.js:674:5
at handleCallback (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/node_modules/mongodb-core/lib/cursor.js:171:5)
at nextFunction (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/node_modules/mongodb-core/lib/cursor.js:682:5)
[nodemon] app crashed - waiting for file changes before starting...
Thanks for any help.
EDIT: I should mention the error only occurs the 2nd time I toggle the button. I am assuming as this has been removed from DB for some reason. The name of and ID persist, yet the onStatus doesn't.
EDIT 2: Full code https://github.com/Sacki2013/homeAutomation
You are trying send the response even after it is already sent. All you have to do is that add return statements after your response is sent.
var express = require('express');
var router = express.Router();
var database = require('../config/database');
var mongojs = require('mongojs');
var db = mongojs(database.url, ['devices'])
// GET : All devices
router.get('/devices', function(req, res, next){
db.devices.find(function(err, devices){
if(err) {
res.send(err);
return;
}
res.json(devices);
});
});
// GET : Single device
router.get('/device/:id', function(req, res, next){
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
// POST : Save a device
router.post('/device', function(req, res, next){
var device = req.body;
device.onStatus = false;
if(!device.name) {
res.status(400);
res.json({"error": "Please add a name."});
} else {
db.devices.save(device, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
}
});
// DELETE : A device
router.delete('/device/:id', function(req, res, next){
db.devices.remove({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
// PUT : Update a device
router.put('/device/:id', function(req, res, next){
var device = req.body;
var updatedDevice = {};
if(device.name) {
updatedDevice.name = device.name;
}
if(!updatedDevice){
res.status(400);
res.json({'Error': 'Name not specified'});
} else {
db.devices.update({_id: mongojs.ObjectId(req.params.id)}, updatedDevice, {}, function(err, device){
if(err) {
res.send(err);
return;
}
/*
* Commenting following line because
* you are sending a response in `findOne`
*/
// res.json(device);
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
}
});
module.exports = router;
On your /device/:id PUT endpoint you're doing the update and findOne calls asynchronously, independent from each other, so res.json() is called twice. Try moving your findOne function inside the update callback.
Related
i'm trying to learn how to build an api with Node and Express.js. I've found the next step by step: click here
And created a very similar version but with my data:
var express = require("express");
var bodyParser = require("body-parser");
var sql = require("mssql");
var app = express();
app.use(bodyParser.json());
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, contentType,Content-Type, Accept, Authorization");
next();
});
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
var dbConfig = {
user: "myUser",
password: "myPass",
server: "myServer",
database: "MyDB"
};
var executeQuery = function(res, query){
sql.connect(dbConfig, function (err) {
if (err) {
console.log("Error al conectarse a la base :- " + err);
res.send(err);
}
else {
// create Request object
var request = new sql.Request();
// query to the database
request.query(query, function (err, res) {
if (err) {
console.log("Error al correr query en la base :- " + err);
res.send(err);
}
else {
res.send(res);
}
});
}
});
}
//GET API
app.get("/api/ApiRequestData", function(req, res){
var query = "select * from [RequestData]";
executeQuery (res, query);
});
After create the server.js doc, executed with npm install and run with node server.js, i opened on postman using the next url: http://localhost:8080/api/ApiRequestData and get an error message: "Could not get any response". On the Node command prompt i get the message:
TypeError: res.send is not a function
at C:\Users\API\server.js:43:44
at C:\Users\API\node_modules\mssql\lib\main.js:1588:20
at Request.userCallback (C:\Users\API\node_modules\mssql\lib\tedious.js:853:61)
at Request.callback (C:\Users\API\node_modules\tedious\lib\request.js:33:27)
at Connection.message (C:\Users\API\node_modules\tedious\lib\connection.js:1179:27)
at Connection.dispatchEvent (C:\Users\API\node_modules\tedious\lib\connection.js:519:45)
at MessageIO. (C:\Users\API\node_modules\tedious\lib\connection.js:439:23)
at emitNone (events.js:106:13)
at MessageIO.emit (events.js:208:7)
at ReadablePacketStream. (C:\Users\API\node_modules\tedious\lib\message-io.js:92:15)
Someone knows why shows this message?
Hope you can help me.
You are shadowing res from line var executeQuery = function(res, query){... with res from line request.query(query, function (err, res) {.... Just rename the last res to something else and you won't get this error:
request.query(query, function (err, result) {
if (err) {
console.log("Error al correr query en la base :- " + err);
res.send(err);
}
else {
res.send(result);
}
});
You can use below query for fetching the records with hard coded query like.
I used same for my application.
sql.connect(config).then(() => {
return sql.query`select * from [dbo].[Customer]`
}).then(result => {
console.log(result)
}).catch(err => {
console.log(err);
})
Fetch the result using store procedure.
sql.connect(config).then(pool => {
return pool.request().input('Customerid', sql.Int, 2).execute("GetCustomerbyId")
}).then(result => {
console.log(result)
}).catch(err => {
console.log(err);
})
My problem is that I can enter data from my input boxes into an SQL table, but the problem is that the text from the table will not show on the page i want to. What works is that the playlist page will show the a href links depending on how many playlists i have made just not the text.
I have left out the database details for security reasons.
playlist.jade
extends layout
block content
h1= title
p Welcome to #{title}
h1 My Playlists
br
a(href='/login') Login
br
a(href='/users/createPlaylist') Create a new Playlists
br
a(href='/users/playlistCreated') test
br
for playlist, i in playlists
a(href='/users/playlistCreated') | #{playlist.text}
br
users.js
var express = require('express');
var mysql = require('mysql');
var router = express.Router();
var dbConnectionInfo = {
host : '',
user : '',
password : '',
database : 'audio_collections'
};
router.get('/createPlaylist', function(req, res, next) {
res.render('new_playlist');
});
router.get('/playlistCreated', function(req, res, next) {
res.render('name_of_created_playlist');
});
router.get('/songCreated', function(req, res, next) {
res.render('song_page');
});
router.get('/newSong', function(req, res, next) {
res.render('new_song');
});
router.post('/newPlaylist', function(req, res, next) {
var dbConnection = mysql.createConnection(dbConnectionInfo);
dbConnection.connect();
dbConnection.on('error', function(err) {
if (err.code == 'PROTOCOL_SEQUENCE_TIMEOUT') {
// Let's just ignore this
console.log('Got a DB PROTOCOL_SEQUENCE_TIMEOUT Error ... ignoring ');
} else {
// I really should do something better here
console.log('Got a DB Error: ', err);
}
});
var playlist = {
text: req.body.thePlaylist
};
dbConnection.query('INSERT INTO Playlists (playlist_name) VALUES(?)', [playlist.text], function(err, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
if (err) {
throw err;
}
// notice that results.insertId will give you the value of the AI (auto-increment) field
playlist.id = results.insertId;
// Going to convert my joke object to a JSON string a print it out to the console
console.log(JSON.stringify(playlist));
// Close the connection and make sure you do it BEFORE you redirect
dbConnection.end();
res.redirect('/');
});
router.post('/newSongAdded', function(req, res, next) {
var dbConnection = mysql.createConnection(dbConnectionInfo);
dbConnection.connect();
dbConnection.on('error', function(err) {
if (err.code == 'PROTOCOL_SEQUENCE_TIMEOUT') {
// Let's just ignore this
console.log('Got a DB PROTOCOL_SEQUENCE_TIMEOUT Error ... ignoring ');
} else {
// I really should do something better here
console.log('Got a DB Error: ', err);
}
});
var song = {
text: req.body.theSong,
url: req.body.theSongURL
};
dbConnection.query('INSERT INTO Songs (song_name, song_url) VALUES(?,?)',[song.text, song.url], function(err, results,fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
if (err) {
throw err;
}
// notice that results.insertId will give you the value of the AI (auto-increment) field
song.id = results.insertId;
// Going to convert my joke object to a JSON string a print it out to the console
console.log(JSON.stringify(song));
// Close the connection and make sure you do it BEFORE you redirect
dbConnection.end();
res.redirect('/');
});
});
});
module.exports = router;
index.js
var express = require('express');
var mysql = require('mysql');
var router = express.Router();
var dbConnectionInfo = {
host : '',
user : '',
password : '',
database : 'audio_collections'
};
/* GET home page. */
router.get('/login', function(req, res, next) {
res.render('login');
});
router.post('/login', function(req, res, next) {
var username = req.body.username;
username = username.trim();
if (username.length == 0) {
res.redirect('/login');
}
else {
req.session.username = username;
res.redirect('/');
}
});
router.get('/', function(req, res, next) {
var dbConnection = mysql.createConnection(dbConnectionInfo);
dbConnection.connect();
dbConnection.on('error', function(err) {
if (err.code == 'PROTOCOL_SEQUENCE_TIMEOUT') {
// Let's just ignore this
console.log('Got a DB PROTOCOL_SEQUENCE_TIMEOUT Error ... ignoring ');
} else {
// I really should do something better here
console.log('Got a DB Error: ', err);
}
});
dbConnection.query('SELECT * FROM Playlists', function(err, results, fields){
if (err) {
throw err;
}
var allPlaylists = new Array();
for (var i = 0; i < results.length; i++) {
var playlist = {
id: results[i].id,
text: results[i].text
};
console.log(JSON.stringify(playlist));
allPlaylists.push(playlist);
}
dbConnection.end();
res.render('playlists', {playlists: allPlaylists});
});
router.get('/users/playlistCreated', function(req, res, next) {
var dbConnection = mysql.createConnection(dbConnectionInfo);
dbConnection.connect();
dbConnection.on('error', function(err) {
if (err.code == 'PROTOCOL_SEQUENCE_TIMEOUT') {
// Let's just ignore this
console.log('Got a DB PROTOCOL_SEQUENCE_TIMEOUT Error ... ignoring ');
} else {
// I really should do something better here
console.log('Got a DB Error: ', err);
}
});
dbConnection.query('SELECT * FROM Songs', function(err, results, fields){
if (err) {
throw err;
}
var allSongs = new Array();
for (var i = 0; i < results.length; i++) {
var song = {};
song.id = results[i].id;
song.text = results[i].text;
song.url = results[i].url;
console.log(JSON.stringify(song));
allSongs.push(song);
}
dbConnection.end();
res.render('name_of_created_playlist', {songs: allSongs});
});
});
});
module.exports = router;
new_playlist.jade
extends layout
block content
form(method='POST', action='/users/newPlaylist')
div
label(for='thePlaylist') Name of Playlist
div
textarea(type='text', name='thePlaylist')
br
input(type='submit', name='Add new Playlist')
a(href='/') Cancel
Here is the database and table setups
database and table setup
I would really appreciate the help and I have been stuck on this for a week now.
Got it fixed I didnt need the for loops inside my array
Only needed var allPlaylist = results; Since results is an array already
I was trying to use two different app.param for different paramaters but it dosnt seem to be possible. when the server checks for the first paramater i get it to sequencially check the second paramater and even if it is checking the second paramater in the second app.param, it nullifies the first paramater from working.
app.param('chatroom', function(req, res, next, chatroom) {
Chatroom.findOne({ 'chat.roomname' : chatroom }, function(err, chatroom) {
if(err)
throw err;
if(!chatroom)
return next();
req.chatroom = chatroom;
next();
});
});
app.get('/:chatroom', function(req, res) {
Profile.findOne({ 'pic.username' : req.user.local.username}, function(err, profilepic) {
if(err)
throw err;
res.render('pages/chatroom', {
chatroom : req.chatroom,
profilepic : profilepic,
user : req.user
});
});
});
app.param('username', function(req, res, next, username) {
User.findOne({ 'local.username' : username }, function(err, user) {
if(err)
throw err;
if(!user){
return res.sendStatus(404);
}
req. profile = user;
next();
});
});
app.get('/:username', function(req, res) {
async.parallel([
function(callback) {
Profile.findOne({ 'pic.username' : req. profile.local.username }, function(err, profilepic) {
if(err)
throw err;
callback(null, profilepic);
});
}, function(callback) {
Buddy.find({ 'bud.username' : req. profile.local.username }, function(err, buddy) {
if(err)
throw err;
callback(null, buddy);
});
}, function(callback) {
Buddy.count({ 'bud.username' : req. profile.local.username}, function(err, buddycount) {
if(err)
throw err;
callback(null, buddycount);
});
}, function(callback){
Userpost.find({ 'post.postuser' : req. profile.local.username }).sort({ 'post.date' : 1 }).exec(function(err, userpost) {
if(err)
throw err;
callback(null, userpost);
});
}, function(callback) {
Userpost.count({ 'post.postuser' : req. profile.local.username }, function(err,postcount) {
if(err)
throw err;
callback(null, postcount);
});
}], function(err, results){
if(err)
throw err;
var profilepic = results[0];
var buddy = results[1];
var buddycount = results[2];
var userpost = results[3];
var postcount = results[4];
res.render('pages/visitprofile', {
user : req. profile,
profilepic : profilepic,
buddy : buddy,
buddycount : buddycount,
userpost : userpost,
postcount : postcount
});
}
);
});
So is there I way i can get both parameters to work on the server at the same time. I reorder them and one works with the other one not working. Maybe i can make next() function skip over the other parameter some how?
I'm trying to update an array in one of my database objects.
I'm checking the object before making a put request. But the object won't update in my MongoDB database.
client/entry/newEntry.controller.js:
$scope.save = function(form) {
$scope.submitted = true;
$scope.entry.date = Date.now;
$scope.entry.writer = $scope.getCurrentUser;
$scope.entry.type = 'chapter';
getHighestArticleId();
$scope.entry.articleId = articleId;
if(form.$valid) {
$http.post('/api/entrys', $scope.entry)
.success(function(data){
console.log(' -- posted entry --');
console.log('data: ', data);
$scope.entry = data;
console.log($scope.entry.orphan);
if($scope.entry.orphan == false){
$scope.parent.children.push($scope.entry);
console.log(' -- parent to update --');
console.log($scope.parent);
$http.put('/api/entrys/' + $scope.parent._id)
.success(function(data){
console.log(' -- updated parent --');
console.log(data);
});
}
});
}
};
entry api/entry/index.js:
'use strict';
var express = require('express');
var controller = require('./entry.controller');
var router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.get('/:id/children/', controller.getChildren);
router.get('/type/:type', controller.getByType);
router.get('/type/:type/orphan/:hasParent', controller.getByTypeAndOrphan);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports = router;
api/entry/entry.controller.js:
// Updates an existing entry in the DB.
exports.update = function(req, res) {
if(req.body._id) {
delete req.body._id;
}
Entry.findById(req.params.id, function (err, entry) {
if (err) {
return handleError(res, err);
}
if(!entry) {
return res.send(404);
}
var updated = _.merge(entry, req.body);
updated.save(function (err) {
if (err) {
return handleError(res, err);
}
return res.json(200, entry);
});
});
};
EDIT
routes.js:
/**
* Main application routes
*/
'use strict';
var errors = require('./components/errors');
module.exports = function(app) {
// Insert routes below
app.use('/api/languages', require('./api/language'));
app.use('/api/forums', require('./api/forum'));
app.use('/api/entrys', require('./api/entry'));
app.use('/api/things', require('./api/thing'));
app.use('/api/users', require('./api/user'));
app.use('/auth', require('./auth'));
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get(function(req, res) {
res.sendfile(app.get('appPath') + '/index.html');
});
};
Found the answer. The problem was in three places. The first in my call to the api
$http.put('/api/entrys/' + $scope.parent._id)
.success(function(data){
console.log(' -- updated parent --');
console.log(data);
});
should instead be
$http.put('/api/entrys/' + $scope.parent._id, $scope.parent)
.success(function(data){
console.log(' -- updated parent --');
console.log(data);
});
The second problem was my child object. I passed the entire object, but only needed the id, so my push should change from this
$scope.parent.children.push($scope.entry);
to this
$scope.parent.children.push($scope.entry._id);
Finally my update function itself needed to be informed that I was handling the sub-document. Which meant that I had to add this to the function
// Updates an existing entry in the DB.
exports.update = function(req, res) {
if(req.body._id) {
delete req.body._id;
}
Entry.findById(req.params.id, function (err, entry) {
if (err) {
return handleError(res, err);
}
if(!entry) {
return res.send(404);
}
var updated = _.merge(entry, req.body);
entry.markModified('children');
updated.save(function (err) {
if (err) {
return handleError(res, err);
}
return res.json(200, entry);
});
});
};
I'm trying to send some form data, but I get this error using express.js:
Can't set headers after they are sent.
This is my code so far:
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.send(200);
});
console.log(res.send(user)) //HERE IS WHERE I GET THE ERROR
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});
By console logging res.send(user) you are sending again. You can send once and once only.
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.status(200).send(user);
});
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});