How to conditionate a Query to do not accept non existent fields - mysql

I have a query inside my controller that it is working but it also work when recibe parameters that doesn't exist
I tried to use HTTP status response code but the result is the same it creates an empty array when you manually type a URL
This is the code inside of Controller
function getPosts(req, res) {
var type = req.params.type;
posts.sequelize.query("SELECT forum.id, forum.forum_type, posts.id,
posts.post_topic_author_id, posts.post_topic_title, posts.forum_type_id
FROM posts, forum WHERE posts.forum_type_id = forum.id AND forum.forum_type = ?", { replacements: [type], type: posts.sequelize.QueryTypes.SELECT }).then(posts => {
res.status(200).send({ posts })
}).catch(err => {
res.status(500).send({ message: 'Ocurrio un error' + err })
});
}
This is the code inside my Routes
const postsController = require('../controllers').posts;
module.exports = (app) => {
app.post('/api/post-create', postsController.create);
app.put('/api/post-update/:id', postsController.update);
app.get('/api/post-get/:type', postsController.getPosts);
}
i need to send the user to something like a 404 page for when he manually tries to enter to a URL that doesn't exist

You can use a middleware to handle error.
for example:
Install hbs
npm install hbs
create a directory views and use this code in app Object:
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
Create a error.hbs file in views directory and add the following code in this file:
Message: {{message}}
Status: {{error.status}}
Stack: {{error.stack}}
catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
I hope it can help you.

Related

ReferenceError when trying to use a variable in an EJS template file

I'm having a heap of trouble just trying to get an EJS template file to recognise a variable that stores the rows of an SQLite3 table query in a corresponding .js file. I get a ReferenceError for the variable I used in the EJS file when launching the server and trying to access that route.
For context it's a micro blog project where I'd like authors to have the ability to save draft articles in to a database and for the user to be able to come back and modify or publish them later.
Here's my 'author.js' file:
// Author Page
const express = require("express");
const router = express.Router();
const assert = require('assert');
/**
* #desc retrieves draft articles
*/
router.get("/author-home", (req, res, next) => {
//Use this pattern to retrieve data
//NB. it's better NOT to use arrow functions for callbacks with this library
global.db.all("SELECT * FROM draftArticles", function (err, rows) {
if (err) {
next(err); //send the error on to the error handler
} else {
res.json(rows);
}
});
});
/**
* #desc Renders the author page
*/
router.get("/author", (req, res) => {
res.render("author-home", data);
});
module.exports = router;
In my 'author-home.ejs' file, I'm trying to insert various article properties in a element like so:
<td><% data[0].article_title %> </td>
<td><% data[0].article_subtitle %> </td>
...etc.
Can anyone tell me what I'm doing wrong? I can also post the code for my 'index.js' file if that's helpful. Many thanks in advance
EDIT:
After some suggestions were sent and the scope issue of the 'data' variable was highlighted, I corrected my code in author.js (at least, I believe so) to the following:
// Author Page
const express = require("express");
const router = express.Router();
const assert = require('assert');
router.get('/author-home', (req, res, next) => {
global.db.all('SELECT * FROM draftArticles', function (err, rows) {
if (err) {
console.log("No data found.")
next(err); //send the error on to the error handler
return;
}
res.render('author-home', { data: rows });
});
});
module.exports = router;
However, I still receive a referenceError when trying to access data in my EJS file.
I also tried, as was suggested, to pass static data like so:
let dummyData = "This is test data";
router.get('/author-home', (req, res, next) => {
res.render('author-home', { data: dummyData });
});
Also receiving a referenceError.
This is because you have not defined "data". You need to define it if you want to send an array you can use
How can I pass an array to an ejs template in express?
your code should be like..
// Author Page
const express = require("express");
const router = express.Router();
const assert = require('assert');
router.get('/author-home', (req, res, next) => {
global.db.all('SELECT * FROM draftArticles', function(err, rows) {
if (err || !rows || !rows.length) {
console.log("No data found.")
// also try to log rows here to see what you are getting. does the "rows" have atricle_title etc attributes or not?
next(err || new Error("No Data found!")); //send the error on to the error handler
return;
}
res.render('author-home', {
data: rows
});
});
});
module.exports = router;

Error handler ignored when NODE_ENV=production

I am building a simple REST API with Node/Express, and I'm having a hard time when I deploy it to production. When NODE_ENV=development, everything works as expected. I get back the JSON error and the correct status code. When NODE_ENV=production, I only get back an HTML page with the default error message and nothing else. I can read the status code, but I need to have access to the full JSON payload to identify the errors better. This is my code:
import Promise from 'bluebird'; // eslint-disable-line no-unused-vars
import express from 'express';
import config from './config';
import routes from './routes';
import { errorMiddleware, notFoundMiddleware } from './middlewares/error.middleware';
import mongoose from './config/mongoose.config';
// create app
const app = express();
(async () => {
// connect to mongoose
await mongoose.connect();
// pretty print on dev
if (process.env.NODE_ENV !== 'production') {
app.set('json spaces', 2);
}
// apply express middlewares
app.use(express.json());
// register v1 routes
app.use('/v1', routes);
// catch errors
app.use(notFoundMiddleware);
app.use(errorMiddleware);
// start server
app.listen(config.port, () => console.info(`server started on port ${config.port}`));
})();
export default app;
This is the notFoundMiddleware:
export default (req, res, next) => next(new Error('Not Found'));
This is the errorMiddleware:
const errorMiddleware = (err, req, res, next) => {
console.log('test'); // this works in development, but not in production
const error = {
status: err.status,
message: err.message
};
if (err.errors) {
error.errors = err.errors;
}
if (process.env.NODE_ENV !== 'production' && err.stack) {
error.stack = err.stack;
}
return res.status(error.status || 500).send({ error });
};
If you are runing on production server, try to use some logging provider like "papertrailapp" to see the error occurs in your app.
I've just stumbled upon the same problem. It turned out it's caused by a transpiler optimization applied when building production bundle - this one: https://babeljs.io/docs/en/babel-plugin-minify-dead-code-elimination
Express' error handlers should have the signature (err, req, res, next) => { ... } (be of arity 4). In your example next is not used anywhere in errorMiddleware function body and thus it gets eliminated (optimized-out) from function signature in production code.
Solution:
use keepFnArgs: true plugin option - possibly through https://webpack.js.org/plugins/babel-minify-webpack-plugin/ webpack configuration:
var MinifyPlugin = require("babel-minify-webpack-plugin")
module.exports = {
// ...
optimization: {
minimizer: [
new MinifyPlugin({
deadcode: {
keepFnArgs: true,
},
}, {}),
],
}
// ...
}
or alternatively pretend in your code that this argument is used:
const errMiddleware = (err, req, res, _next) => {
// ... your code ...
// ...
// cheat here:
_next
}

Is there a way to include jade file directly in html file?

How can I combine a node.js website that its frontend is based on html with another node.js website that its frontend is based on jade template engine? I am using Express framework.
On the frontend there are four files: index.html, index2.html, chat1.html, chat2.html, which are located in the public folder. The blog website that I want to add to this website has only jade template engine, which are located in the views folder.
The index.html (which is in public folder) is the entry point to the home page of the website. When from index.html I refer to index3.jade, which is the Home page of the second app, i.e., blog jade app, Chrome browser states: "404 Not Found". However, I can go to the other two pages of the blog jade website, i.e., Add Post and Add Category. It is only the Home page of the blog jade app that is not being displayed.
So, I am not able to see only the Home page of the blog jade app, which starts at the root directory. Both the html app and the blog jade app start at the root directory. I was able to make the blog jade app to be displayed at the root directory, but then I could not see the html app, which also starts at the root directory.
Here is how I referred to each file from index.html front page:
`<li>gallery</li>`
`<li>chat</li>`
`<li>blog</li>`
Is there a way to have the home page of the blog jade app to be displayed at a directory other than the root directory?
Here is the related app.js code:
// Gallery HTML Code
var routes = require('./');
app.get('/public/index.html');
// Blog Code
var mongo = require('mongodb');
var db = require('monk')('localhost/nodeblog');
var routes = require('./');
var routes = require('./routes/index3');
var posts = require('./routes/posts');
var categories = require('./routes/categories');
var app = express();
app.locals.moment = require('moment');
app.locals.truncateText = function(text, length) {
var truncatedText = text.substring(0, length);
return truncatedText;
}
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Express Session
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true
}));
// Express Validator
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.'),
root = namespace.shift(),
formParam = root;
while (namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param: formParam,
msg: msg,
value: value
};
}
}));
// Connect-Flash from Express-Messages
app.use(flash());
app.use(function(req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
// Make our db accessible to our router
app.use(function(req, res, next) {
req.db = db;
next();
});
app.use('/index3', routes);
app.use('/posts', posts);
app.use('/categories', categories);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
Here is the related code from index3.js in the routes folder:
router.get('index3', function(req, res, next) {
var db = req.db;
var posts = db.get('posts');
posts.find({}, {}, function(err, posts) {
res.render('index3', { posts: posts });
});
});
module.exports = router;
Here is the related code from index.js in the routes folder:
router.get('/', function(req, res, next) {
res.render('public/index.html');
});
module.exports = router;

How to display JSON file with Node.JS/Express

I'm VERY new to Node.js... so this is probably going to be stupid, basic. Here is what I am trying to do: I want to create a Node.js app that will query my MySQL database and return a JSON file to the user.
So far I have very little :) I have a project created with Webstorm. I have an index.js file and an index.ejs file. The index.js file has the following:
var express = require('express');
var router = express.Router();
var appdata = require('../data.json');
var mysql = require('mysql');
// http://nodejs.org/docs/v0.6.5/api/fs.html#fs.writeFile
var fs = require('fs');
var connection = mysql.createConnection({
host: 'xxxxxx',
user: 'xxxxx',
password: 'xxxxx'
database: 'xxxxx';
});
connection.connect();
router.get('/', function(request,response) {
connection.query('select AProgram_UID as UID, SiteDescription as Program, IcStatus as Status from AP_Details;', function (err, results, fields) {
if (err) {
console.log('Error in Query', err.message);
return response.send(500, err.message);
};
return JSON.stringify(results);
connection.end();
});
});
I haven't defined what goes in the index.ejs file because I really don't know where to go from here. I can write the JSON out to file from the code shown if I use writeFile, so I know the database part is correct.
Hopefully I explained enough... as mentioned, I'm new to Node. I just want to do something 'real' with it and this is something I need on a project I have.
Thanks!
In your router.get callback return the JSON back to the requester by using res.json to properly assign the Content-Type header to application/json and stringify whatever is passed to it.
Also you want to remove your return statements to before connection.end() otherwise connection.end() will never be called.
router.get('/', function(req, res) {
connection.query('select AProgram_UID as UID, SiteDescription as Program, IcStatus as Status from AP_Details;', function (err, results, fields) {
if (err) {
console.log('Error in Query', err.message);
res.status(500).send(err.message);
}
else
// render index view and pass in results JSON
res.json(results);
return connection.end()
});
});
Edit to use EJS View Engine Rendering
In order to use EJS you need to have your View Engine set to EJS and have a default Views directory setup. In your main Express server file it should look something like this before any routes
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
You'll need to change the code above from using res.json to use res.render. You'll also need to pass your results into the render function so the index.ejs can access the results JSON
res.render('index', { results: results });
In your index.ejs file you can access results using the EJS markup syntax
<html>
<body>
<p><% results %></p>
</body>
</html>

passport send error by json

I'm making an app with express + passport and angularJS; I want to be able to send any errors produced from passport (such as username taken or no email provided) by json so my angularJS app can receive these errors in a json response. More specifically right now I want to have a json response to my signup POST method that outputs any errors. I have tried to do this for myself and I've search all over the web and stack overflow I just cannot work this out!
Here is my users route file in express:
var express = require('express');
var router = express.Router();
var isAuthenticated = require('../config/isAuthenticated');
module.exports = function(passport){
router.get('/loggedin', function(req, res){
res.send(req.isAuthenticated() ? req.user : '0');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/signup',
failureFlash: true
}));
router.post('/login', passport.authenticate('local-login'), function(req, res){
res.send(req.user);
});
router.post('/signout', function(req,res){
req.logout();
res.json({redirect: '/'});
});
router.get('/authtest', isAuthenticated, function(req, res){
res.render('authtest', {user: req.user});
});
return router;
};
This is my passport signup strategy:
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done){
process.nextTick(function(){
User.findOne({'local.username' : username}, function(err, user){
if(err) return done(err);
if (user) { //username already exists
return done(null, false, {message: 'Username already exists'});
} else if(!req.body.email) { //no email address provided
return done(null, false, {message: 'You must provide an email address!'});
} else {
var newUser = new User();
newUser.local.username = username;
newUser.generateHash(password, function(err, hash){
if(err) return done(err);
newUser.local.password = hash;
});
newUser.email = req.body.email;
newUser.servers = [];
newUser.save(function(err){
if(err) throw err;
return done(null, newUser);
});
};
});
});
}
));
I know looking at my code right now it looks like I haven't tried to solve this myself at all but this is just my latest working code; I have been stuck at this for the past few days!
Any help would be greatly appreciated :)
According to the current code of passport this is probably achievable by passing custom callback to handle all results of authentiction yourself. This callback is given after options or instead of those.
passport( "local-signup", { ... }, callbackFn );
or
passport( "local-login", callbackFn );
This callback is used in all resulting situations of trying to authenticae. It is thus invoked on processing errors like this:
callbackFn( err )
If (all configured) authentications have failed it is called with
callbackFn( null, false, challenge(s), status(es) )
On successfully having authenticated user the callback is invoked like so:
callbackFn( null, user, infos )
with infos optionally provided by strategies.
Now comes the bottom-side: In either situation passport.authenticate() skips usual processing but instantly invokes provided callback to care for the rest. This includes processing of any options passed in call for passport.authenticate() like flashing messages, preparing session and request for containing authenticated user etc.
Since options given passport.authenticate() are never passed into callback there is actually no obvious reason to use both.
When I was stumbling over the very same problem (linking passport-service with angular-js POST request) I declined to consider use of callback a proper solution. This callback isn't documented. And it doesn't even look quite useful for it isn't passing req, res and next to pass any actual request in callback. Thus it makes very little sense to use it at all and I'd expect it to vanish soon or to change its behaviour quite much.
So the second approach was about trying to figure out why there is a problem in AngularJS. Passport is sending plain text Unauthorized in response with status code 401. AngularJS is trying to parse this as JSON and produces Syntax error. The text Unauthorized results from passprt ending response very simply by invoking
res.statusCode = 401;
res.end(http.STATUS_CODES[res.statusCode]);
Thus a proper workaround might try to replace
either text in http.STATUS_CODES though this is having impact on processing further requests and thus isn't preferrable
or res.end() by an overloaded method acting differently if res.statusCode is 401.
Due to affecting any current request, only, I tried the latter. Replaced res.end() might be used to send any text you want:
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
return _end('{"status":"Unauthorized"}');
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Alternatively the replaced method might add previously missing response header information on content type, for this was actually causing issues in AngularJS processing that response as JSON by default.
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
res.set("Content-Type", "text/plain");
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Finally, either approach is really just a workaround. I think passport is in the need for revising this annoying limitation.