How to embed shrinkroute url() call in jade template hrefs? - node.js

How do I translate the examples from the shrinkroute README file:
// or views...
User profile
User profile
for use in jade templates?
For example, something like
a(href="#{ url( "user", { id: 1 }) }") User profile
Thanks in advance.

First of all, ensure that you're using the shrinkroute middleware:
app.use( shrinkr.middleware );
It'll automatically provide you the following helpers:
req.buildUrl and res.locals.url - builds paths for a route. The same as using shrinkr.url().
req.buildFullUrl and res.locals.fullUrl - builds full URLs for a route. The same as using shrinkr.fullUrl().
In Jade, you simply have to use the following:
a(href=url( "user", { id: 1 } )) My Username
a(href=fullUrl( "user", { id: 1 } )) My Username
Rendered output:
My Username
My Username
The above output will depend on the routes you have named in your shrinkroute instance.
Disclaimer: I'm the creator of Shrinkroute.

here's a general solution for calling a function from within a template; see #gustavohenke 's answer for a specific solution for how to use shrinkroute's built-in locals.buildFullUrl function within a jade template
// node.js
var url = require('url');
// Set up locals.shrinkUrl for every request
app.all('*', function(req, res, next){
res.locals.shrinkUrl = function(path, queryObject){
var out = url.format({
pathname: path,
query: queryObject,
});
return out;
};
next();
});
// template.jade
a(href=locals.shrinkUrl("user", {id: 1}) ) User profile
// rendered
<a href='/user?id=1'>User profile</a>

Related

Pug Express req.user Interpolation

I am having a really hard time accessing a variable in Pug from Express.
My route looks like:
router.get('/', ensureAuthenticated, function(req, res, next) {
res.render('profile/profile', {user: req.user});
});
My template looks like:
.card
.card-body
h4.card-title Local Profile
if (user)
p.card-text
strong.pr-2 ID: !{user.userEmail}
br
br
strong.pr-2 Name:
= user
br
br
strong.pr-2 Email:
= user
br
br
strong.pr-2 Password:
span.text-muted= user
a.btn.btn-default(href="/profile/edit") Edit
p.card-text
small.text-muted Last login
= user
The user object looks like:
{UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
If I include just user without stringifying, then I get Object object. If I stringify I can output the object, but trying to access a property in the object gives me nothing.
But if I
console.log(x.userEmail)
after
var x = !{JSON.stringify(user)}
then I get the property.
Any help would be fantastic!!
use user.userEmail instead of !{user.userEmail}
this worked fine for me,
app.get('/', function(req, res, next) {
var u = {UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
res.render('index', { user: u });
});
,
html
head
title= user.userEmail
body
h1= user.userEmail
The issue actually had nothing to do with Pug or Express. It was with the way that Passport was setting the req.user variable after finding the user using Bookshelf.js.
There were some additional nested attributes in the object. This separate answer led me to a solution to isolate the information I needed. Cannot get properties of req.user
The way to access the information in Pug is the same as laid out in the docs.

Can't access data from a POST call by express saved by Mongoose

I am just learning Node, Mongoose and Express. I've looked all through stackoverflow and else where, and I still cannot find an answer to my question.
I'm fairly certain this a just a very beginner mistake. When I save posted data inside of app.post, I cannot access it anywhere else in my node/express code. I get the error "ReferenceError: getUsersInfo is not defined."
var mongoose = require('mongoose');
mongoose = mongoose.createConnection('localhost', '27017');
mongoose.on('error', console.error.bind(console, 'connection error:'));
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectID;
var usersSchema = new Schema({
username: String,
password: String,
email: String,
date_created:{ type: Date, default: Date.now }
});
var users = mongoose.model('users', usersSchema);
app.get('/', function(req,res){
res.render('layout.jade', {
title : "Hello!"
,analytics: 'XXXXXX'
});
});
app.post('/', function(req, res){
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body.email,
});
getUsersInfo.save(function (err, getUsersInfo) {
if (err){throw err;}
console.log(getUsersInfo.username);
res.redirect("/success");
});
});
app.get('/success', function(req, res){
res.render('loggedin.jade', {
title : "Admin Panel",
analytics: 'XXXXXX'
});
var username = getUsersInfo("username");
res.write("<h1> hi, " + username + "</h1>");
});
Any help you can give me such as places to look or a better way to write my code would be much appreciated. I tried learning from out-dated tutorials and haven't gotten anywhere and the mongoose api/docs don't seem to cover this thing.
Thanks!
EDIT
To clarify what I'm trying to achieve here is I want to take form inputs (rendered in the layout.jade file) and save them to the database (Which I'm doing in app.post). I want to then be able to access those database objects elsewhere in the code, not specifically in just app.get('/success'). But I keep running into a scope issue, it seems.
In a nodejs/expressjs application, your routes (app.get, app.post etc) are set up with callback functions that will be invoked when a user requests the corresponding URL. All operations that should be done in a request needs to be invoked from within these callbacks. In your example, you're attempting to access getUsersInfo outside the callback you have set up in app.post. If you move the code inside the callback instead, it should get you further:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
res.redirect('/success');
}
else {
// Send error
}
});
});
Edit: Answer above was confused by the indentation of the code, which should be corrected now. It looks like you're making a regular web app, so I would recommend that you check out the session middleware. An easy way to get started with it is to generate your express app with the command express --sessions myapplication. This will set your application so you have a req.session object where you can put session scoped objects:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
req.session.user = userInfo;
res.redirect('/success');
}
else {
// Send error
}
});
});
Then in your app.get
app.get('/success', function(req, res) {
var user = req.session.user;
// Do what you want with the user
});
If you were using a REST approach, a typical pattern when saving and then retrieving a resource is to redirect with GET to the URL fetching the created resource, and include an identifier for the resource. In that case, your redirect would look like this
res.redirect('/success/' + userInfo.id)
And you could make a route like this:
app.get('/success/:userId', function(req, res) {
var user_id = req.param('userId');
users.findById(user_id, function(err, user) {
// user is available here. Add it to the template context and render it.
});
});
The syntax :userId in the uri indicates a path variable named userId which will be available in req.params.
But if you're writing a regular web application, I would recommend the first approach using the session middleware.

Node.js middleware organization and parameter validation

I'm building an express app and I'd like to know how fancy I can get with middleware. Roughly, I want to accomplish the following with middleware.
Done:
Add requestId to all routes
Authenticate request
Check whether a user has access to a given resource (apart from
authentication)
Not done:
A) Validate parameters for a given route
B) Organize middleware in a sane way if it differs from route to route,
and 3 middlewares are called routinely per route
I have defined my middleware in a separate file, and import it into app.js like so:
var middleware = require('./middleware');
var requestId = middleware.requestId;
var authenticate = middleware.authenticate;
To apply it to all routes I add it to express config:
var app = express.createServer();
app.configure(function () {
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(requestId); // add requestId to all incoming requests
});
And for route specifics, I add it as an app.get argument:
var routes = require('./v1/routes');
app.get("/v1/foo", routes.foo);
app.get("/v1/bar", authenticate, routes.bar);
Problem A
I'd love to have middleware that I could use to check parameters
validate('x','y','z')
And use it like so for a given route:
app.get("/v1/bar", authenticate, validate('x','y','z'), routes.bar);
Is there a good way to do this? Or should I just be validating on per route basis inside the route definition files?
Problem B
Is there a better way to organize and use my middleware that I should consider?
Update
I'm looking for a way to validate parameters that change a lot between routes. The below obviously don't work- I cannot pass params into the middleware- but is there way where I can define middleware that does this and call it as I've said above?
var validateParams = function (req, res, params, callback) {
// Make sure the required parameters are in the request
console.log('checking for params '+params);
for (var i = 0; i < params.length; i++) {
var param = params[i];
if(!(param in req.query)){
logger.info('cannot find param ['+param+'] in req: '+JSON.stringify(req.query));
res.writeHead(400, {
"Content-Type": "application/json"
});
var out = {
"err": "request missing required parameters"
};
res.end(JSON.stringify(out));
return;
}
}
callback();
}
Problem A
app.get("/v1/bar", authenticate, validate, routes.bar);
function validate(req,res,next){
//Get all parameters here by req.params and req.body.parameter
//validate them and return.
if(validation_true)
next()
}
Problem B
You can use middleware in a way that you don't always need to call authenticate and validate they are called automatically. But that can lead to a mess, for ex. Your middleware then would run on every call, so for SIGNUP/REGISTER there is no point running authenticate.
With validate, sometimes you would need to validate email, sometimes phone no. so both cannot go along.
So using them separate on every call seems the BEST way to me.
You can use express-validation to validate body, query, params, headers and cookies of a request. It responds with errors, if any of the configured validation rules fail.
var validate = require('express-validation'),
Joi = require('joi');
app.post('/login', validate({
body: {
email: Joi.string().email().required(),
password: Joi.string().regex(/[a-zA-Z0-9]{3,30}/).required()
}
}), function(req, res){
res.json(200);
});
This will check if the email and password body params matches the validation rules.
If validation fails it will respond with the following error.
{
"status": 400,
"statusText": "Bad Request",
"errors": [
{
"field": "password",
"location": "body",
"messages": [
"the value of password is not allowed to be empty",
"the value of password must match the regular expression /[a-zA-Z0-9]{3,30}/"
],
"types": [ "any.empty", "string.regex.base" ]
}
]
}
You can also check my repo express-mongoose-es6-rest-api for complete integration.
You could also use a higher-order function (function that returns a function). Thereby passing an array of endpoint specific params to check.
module.export = Class RequestValidator {
static validate(params) {
return function(req, res, next){
for(const param of params) {
validateYourParams here...
if (validation fails) {
return next(new Error());
}
}
next();
}
}
}
And within your routeDefinition you can now call the validation middleware and pass route specific arguments to it.
const RequestValidator = require('your-validation-middleware');
const controller = require('your-controller');
app.post('/path')
.RequestValidator.validate(
[{
name: 'paramName',
type: 'boolean'
},
{
name: 'paramName2',
type: 'string'
}
])
.Controller.handleRequest;

Why does req.params return an empty array?

I'm using Node.js and I want to see all of the parameters that have been posted to my script. To get to my function, in my routes/index.js I'm doing:
app.post('/v1/order', order.create);
Then in my function, I have:
exports.create = function(req, res, next) {
console.log( req.params );
But it's returning an empty array. But when I do:
exports.create = function(req, res, next) {
console.log( req.param('account_id') );
I get data. So I'm a bit confused as to what's going on here.
req.params
can only get the param of request url in this pattern:/user/:name
req.query
get query params(name) like /user?name=123 or body params.
req.params only contain the route params, not query string params (from GET) and not body params (from POST). The param() function however checks all three, see:
http://expressjs.com/4x/api.html#req.params
I had a similar problem and thought I'd post the solution to that for those coming here for the same reason. My req.params was coming out as an empty object because I declared the URL variable in the parent route. The solution is to add this option to the router:
const router = express.Router({ mergeParams: true });
With postman, you can have two types of get requests:
Using x-www-form-urlencoded and passing data through the body.
Using url parameters
You can always use this code snippet to always capture the data, no matter how you pass it.
/*
* Email can be passed both inside a body of a json, or as
a parameter inside the url.
* { email: 'test#gmail.com' } -> test#gmail.com
* http://localhost/buyer/get/?email=test#gmail.com -> test#gmail.com
*/
let { email }: { email?: string } = req.query;
if (!email) email = req.body.email;
console.log(email);
Add this for your server.js or app.js:
app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded

Node.js / Express make variable available in layout on all routes

Once a user has logged in, I want to display their username in the header, which is currently part of layout.jade. Their details are in req.currentUser but req isn't accessible from the layout.
I know I can do the following for each of the render calls in my routes:
res.render('help', {
locals: { currentUser: req.currentUser }
});
But it seems there must be a better way than adding { currentUser: req.currentUser } into the locals every single one of my routes.
I'm still a Node newbie, so apologies if this is a stupid question.
You need to use a dynamic helper. It is a function that is passed the request and response objects, just like any route or middleware, and can return data specific to the current request. (including session data)
So, in app.js:
app.dynamicHelpers({
currentUser: function (req, res) {
return req.currentUser;
}
});
and in your template:
<div><%= currentUser %></div>

Resources