PUT and DELETE always route to GET in Node + Express - node.js

I'm a beginner in Node/Express. I tried to make an CRUD application but stuck at update and delete. I think my router code is problematic but I don't know why. The following code is in my controller, everything works but PUT and DELETE. It always route to GET. I tried to use next(); but it returns this error: Can't set headers after they are sent..
I can make the delete works by using GET /:company_id/delete but it's not a good and standardized solution. How can I get update and delete process worked?
'use strict';
var Companies = require('../../models/companies');
module.exports = function (router) {
// INDEX
// accessed at GET http://localhost:8000/companies
router.get('/', function (req, res) {
Companies.find(function(err, model) {
if (err) {
res.send(err);
}
else {
res.format({
json: function () {
res.json(model);
},
html: function () {
res.render('companies/index', model);
}
});
}
});
});
// CREATE VIEW
// accessed at GET http://localhost:8000/companies/create
router.get('/create', function (req, res) {
res.render('companies/create');
});
// CREATE DATA
// accessed at POST http://localhost:8000/companies
router.post('/', function (req, res) {
var name = req.body.name && req.body.name.trim();
var type = req.body.type && req.body.type.trim();
// VALIDATION
if (name === '') {
res.redirect('/companies/create');
return;
}
var model = new Companies({name: name, type: type});
model.save(function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
});
// READ
// accessed at GET http://localhost:8000/companies/:company_id
router.get('/:company_id', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
res.render('companies/read', model);
}
});
});
// UPDATE VIEW
// accessed at GET http://localhost:8000/companies/:company_id/edit
router.get('/:company_id/edit', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
res.render('companies/edit', model);
}
});
});
// UPDATE DATA
// accessed at PUT http://localhost:8000/companies/:company_id
router.put('/:company_id', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
model.name = req.body.name;
model.type = req.body.type;
model.save(function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
}
});
});
// DELETE
// accessed at DELETE http://localhost:8000/companies/:company_id
router.delete('/:company_id', function (req, res) {
Companies.remove({ _id: req.params.company_id }, function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
});
};

HTML forms only support GET and POST. XMLHTTPRequest supports PUT and DELETE however, so you may have to go that route OR use something like method-override to allow HTML forms to submit using other HTTP verbs.

Related

How to reuse database controllers

I'm trying to reuse my controllers which handle database operations. I'm bit struggling with structuring my application. Here's what I have:
server.js
var apiController = require('./controllers/api');
router.get('/cars', function (req, res) {
// get all cars from DB and render view
apiController.getCars().then(function (cars) {
res.render('index', {cars: cars});
});
});
router.get('/api/cars', function (req, res) {
// get all cars from DB and return JSON
apiController.getCars().then(function (cars) {
res.json(cars);
});
});
controllers/api.js
module.exports = {
getCars: function () {
db.collection('cars').find().toArray( function (err, cars) {
if (err) throw err;
return cars;
});
},
// tried also something like this but this doesn't really work
// for my use case because I don't want to attach any particular
// res to the function
getCars: function (req, res, next) {
db.collection('cars').find().toArray( function (err, cars) {
if (err) throw err;
res.json(cars);
});
},
};
Your current problem is that you expect promises as return in server.js while you use callbacks in the controller. I suggest you change your function getCars to return a Promise. Don't know what ODM/ORM you're using but it might look like something like this:
getCars: function () {
return db.collection('cars').find();
},
server.js
var apiController = require('./controllers/api');
router.get('/cars', function (req, res) {
apiController.get('cars').then(function (cars) {
res.render('index', {cars: cars});
});
});
router.get('/api/cars', function (req, res) {
apiController.get('cars').then(function (cars) {
res.json(cars);
});
});
controllers/api.js
var Promise = require('bluebird');
module.exports = {
get: function (modelName) {
return new Promise(function(resolve,reject){
return db.collection(modelName).find().toArray(function(err, models){
if (err) {
return reject(err);
}
else {
return resolve(models);
}
});
});
}
};
server.js
var apiController = require('./controllers/api');
router.get('/cars', apiController.getCars);
controllers/api.js
function getCarsAsync(req, res, next){
db.collection('cars').find().then(function(carsData){
if(carsData){
return res.send(carsData);
}
else{
return res.status(401).send('User is not authorized');
}
}).catch(function(err){
return next(err);
});
}
module.exports = {
getCars: getCarsAsync
};

RESTful API singular route with a single object to retrive and update (options parameters)

Hi i'm stucked trying to create a route in the RESTful API server in express.
I've configured other routes and now i need to configure an ('/options) or ('/profile') singular route where there is only one object to retrive and update.
Basically i need to do the same of the json-server module in the Singular routes section.
So when i visit the /options endpoint i got the predefined object with this schema
{
tax: Number,
inps: Number,
ritenuta: Number,
banca: {
nome: String,
iban: String
}
}
to update.
Here's my actual routes for /options:
var Option = require('../models/option');
var express = require('express');
var router = express.Router();
router.route('/options')
.get(function(req, res) {
Option.find(function(err, options) {
if (err) {
return res.send(err);
}
res.json(options);
});
})
.post(function(req, res) {
var option = new Option(req.body);
option.save(function(err) {
if (err) {
return res.send(err);
}
res.send({message: 'Option Added'});
});
});
// Save an option
router.route('/options/:id').put(function(req, res) {
Option.findOne({ _id: req.params.id}, function(err, option) {
if (err) {
return res.send(err);
}
for (prop in req.body) {
option[prop] = req.body[prop];
}
option.save(function(err) {
if (error) {
return res.send(err);
}
res.json({message: 'Option updated!'})
});
});
});
// Retrive an option
router.route('/options/:id').get(function(req, res) {
Option.findOne({ _id: req.params.id }, function(err, option) {
if (err) {
return res.send(error);
}
res.json(option);
});
});
// Delete an option
router.route('/options/:id').delete(function(req, res) {
Option.remove({ _id: req.params.id}, function(err, option) {
if (err) {
return res.send(err);
}
res.json({message: 'Option deleted!'});
});
});
module.exports = router;
but it's much complicated. It should be simpler. In fact, in this case i need to get all the options, get the id of options[0] and make a call with the id as params to retrive the object and update.
Any suggestions please?

Why my nodejs requests are slow?

Im new in nodejs, and Im trying to learn by creating an app that has a list of users, that I can add and remove those users. Im using angularjs in frontend to send request to nodejs and after that to mongodb. The problem is that, if I click a lot of times in the button "adduser" a lot of times, my app goes slow.
To interact to mongodb I use:
app.get('/users',function (req, res) {
mongoose.model('Usuario').find(function (err, list) {
res.send(list);
});
});
app.post('/addusuario', function (req,res) {
var usuario = new Usuario(req.body);
usuario.save(function (err) {
if (err) {
console.log(err);
} else {
console.log('Usuario salvo com sucesso');
}
}); });
app.delete('/delusuario/:id', function (req, res) {
var id = req.params.id;
mongoose.model('Usuario').findByIdAndRemove(id , function(err) {
if(err) {
console.log(err);
} else {
console.log('Usuario removido com sucesso!');
}
});
});
Im my angularapp:
app.controller('AppCtrl', function($scope, $http, Data) {
function reload() {
Data.get('users').then(function(data){
$scope.usuarios = data;
console.log(data);
});
};
$scope.addUsuario = function(usuario) {
Data.post('/addusuario', usuario);
reload();
};
$scope.deletarUsuario = function(id) {
Data.delete("/delusuario/"+id).then(function(result) {
});
reload();
};
reload();
});
I dont know why it is becaming slow after I click to add user more than 3 times..
What I see in your code that you are not sending an response back to the user, you should do something after insert or delete in the database. res.end();
You should rewrite your code in the following way:
app.get('/users',function (req, res) {
mongoose.model('Usuario').find(function (err, list) {
res.send(list);
});
});
app.post('/addusuario', function (req,res) {
var usuario = new Usuario(req.body);
usuario.save(function (err) {
if (err) {
console.log(err);
res.json({err: err});
} else {
res.json({ok: true});
console.log('Usuario salvo com sucesso');
}
}); });
app.delete('/delusuario/:id', function (req, res) {
var id = req.params.id;
mongoose.model('Usuario').findByIdAndRemove(id , function(err) {
if(err) {
console.log(err);
res.json({err: err});
} else {
res.json({ok: true});
console.log('Usuario removido com sucesso!');
}
});
});
You block the stack by not returning the response to the client. And this is most probably the cause of your slow request.

MEAN Stack CRUD update 404 error

Heads up, noob coming through. Trying to build a MEAN stack todo list. So far I've gotten everything to work except for the update option. What I've done is set up the application so that it prompts the user to write in the item they want to update. So say they add the item 'ddd.' The update button would then appear beside the item, and then the user would be given a prompt, asking them to enter the new item. The problem is whenever the user does in fact enter the new item to replace the old, nothing happens, and I instead get a 404 error in the command prompt. Any help would be much appreciated. Below you'll find my controller, routes, index.html
routes/api
var Todo = require('./models/todo');
module.exports = function(app) {
// api ---------------------------------------------------------------------
// get all todos
app.get('/api/todos', function(req, res) {
// use mongoose to get all todos in the database
Todo.find(function(err, todos) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.json(todos); // return all todos in JSON format
});
});
// create todo and send back all todos after creation
app.post('/api/todos', function(req, res) {
// create a todo, information comes from AJAX request from Angular
Todo.create({
text : req.body.text,
done : false
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});
// delete a todo
app.delete('/api/todos/:todo_id', function(req, res) {
Todo.remove({
_id : req.params.todo_id
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});
//update to do
app.put('/api/todos/_id', function(req, res) {
Todo.findById(req.params._id, function(err, todos){
todo.text = req.body.text;
console.log(todos);
todos.save(function() {
if (!err) {
res.send(todos);
} else if (err) {
res.send(err);
}
});
});
});
// application -------------------------------------------------------------
app.get('*', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
};
controller
var baselTodo = angular.module('baselTodo', []);
function mainController($scope, $http) {
$scope.formData = {};
// when landing on the page, get all todos and show them
$http.get('/api/todos')
.success(function(data) {
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
// when submitting the add form, send the text to the node API
$scope.createTodo = function() {
$http.post('/api/todos', $scope.formData)
.success(function(data) {
$('input').val('');
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
// delete a todo after checking it
$scope.deleteTodo = function(id) {
$http.delete('/api/todos/' + id)
.success(function(data) {
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
$scope.updateTodo = function(id) {
$scope.newItem = prompt("Please enter your new item:", "");
$http.put('/api/todos/' + id, {formData: $scope.newItem}).success(function(data) {
$scope.todos = data;
});
$http.get('/api/todos').success(function(data) {
$scope.todos = data;
});
};
};
It looks like, in your routes api:
app.put('/api/todos/_id', function(req, res) {
You forgot the colon in the path, therefore you can't access that variable. Try:
app.put('/api/todos/:_id', function(req, res) {

NodeJS - showing different content for logged in or not users

I'm trying to show defferent content for logged in and not users on one page.
Here is the code I use for generating / page:
app.get('/',function(req, res){
if (!checkSession(req, res)) {
res.render('index.ejs', {
title: 'FrontSpeak - blog-based social network'
})
} else {
res.render('index.ejs', {
title: 'autrhorized'
})
}
})
checkSession function:
function checkSession(req, res) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
return true;
} else {
return false;
}
});
});
} else {
return false;
}
}
loggin function:
app.post('/', function(req, res){
db.collection("users", function (err, collection) {
collection.findOne({ username: req.body.username }, function (err, doc) {
if (doc && doc.password == req.body.password) {
console.log("user found");
req.session.user_id = doc._id;
}
}
});
});
});
So, it doesn't seems to be working. However, I think this is not the best way to display different content. May be there are some more elegant ways to do this? Thank you!
UPDATE: New login function:
app.post('/', function(req, res){
db.collection("users", function (err, collection) {
collection.findOne({ username: req.body.username }, function (err, doc) {
console.log('found user');
if (doc && doc.password == req.body.password) {
req.session.user_id = doc._id;
res.redirect('/');
};
res.redirect('/');
});
res.redirect('/');
});
});
This is a case of trying to apply the traditional synchronous model to Node's asynchronous callback-driven model.
After your database query completes, you return true, but you're just returning to the database driver. checkSession returned a long time ago. Since that function returns undefined if there is a session.user_id (and false if there isn't), the login check will always evaluate false.
Instead, you can use Brandon's suggestion to make checkSession asynchronous, or I recommend implementing a middleware function:
function checkLogin(req, res, next) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
if (err) return next(err); // handle errors!
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
} else {
req.currentUser = null;
}
next();
});
});
} else {
req.currentUser = null;
next();
}
}
Now you have two ways of using your middleware function. If you want to check for a user on every request, just add it to the app:
app.use(checkLogin);
Now every single request will have a req.currentUser, but you incur the performance hit of fetching login state from the database for every request. Alternatively, if you only need user information for certain requests, stick the function in the route:
app.get('/', checkLogin, function(req, res) {
if (req.currentUser) {
// logged in
} else {
// not
}
});
You can read more about this in the Express docs.
It looks like you're trying to use checkSession as a synchronous function by checking its return value, but checkSession cannot be synchronous because it depends on asynchronous functionality, namely the callback here: db.collection('users', function (err, collection) .... You'll need to modify checkSession to be async:
function checkSession(req, res, callback) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
callback(true);
} else {
callback(false);
}
});
});
} else {
callback(false);
}
}
and then use it asynchronously in your request handler:
app.get('/',function(req, res){
checkSession(req, res, function(isUser) {
if (!isUser) {
res.render('index.ejs', {
title: 'FrontSpeak - blog-based social network'
})
} else {
res.render('index.ejs', {
title: 'autrhorized'
})
}
});
})

Resources