How to access URL segment(s) in ExpressJS - node.js

So let's say we have the following url:
http://example.com/shops/map/search
I want to access the second segment (map) and check its value.
How can I achieve this in Express? Thanks in advance!

you have to configure your express routes to accept url segments.
app.get('/shops/:type/search', function (req, res) {
res.send(req.params)
})
For a request like this
http://example.com/shops/map/search
req.params will contain required URL segment.
Request URL: http://example.com/shops/map/search
req.params: { "type": "map" }

You can access the url segments by splitting the url into an array.
Like this:
let requestSegments = req.path.split('/');

You can use a route parameters with a constant set of values.
Express uses path-to-regexp to parse the strings you provide for routes. That package permits providing a custom pattern with a parameter to limit the values that can be matched.
app.get('/shops/:kind(list|map)/search', searchShops);
The contents of the parenthesis, (...), are a partial RegExp pattern, in this case equivalent to:
/(?:list|map)/
# within a non-capturing group, match an alternative of either "list" or "map" literally
Then, within searchShops, you can determine which value was given with req.params:
function searchShops(req, res) {
console.log(req.params.kind); // 'list' or 'map'
// ...
}
Alternatively, you can leave the parameter open, checking the value within the handler, and invoke next('route') when the value isn't acceptable:
app.get('/shops/:kind/search', searchShops);
var searchKinds = ['list', 'map'];
function searchShops(req, res, next) {
if (!searchKinds.includes(req.params.kind)) return next('route');
// ...
}

The original answer does the job, but leaves you with an empty element in the array. I'd use the following slight variation to solve this too.
let requestSegments = req.path.split('/').filter((s) => { return s !== ''});

Related

Using chain validation to check existence of optional fields with Express Validator

I am trying to check for the existence of an optional field in an API request, and if that field exists, perform a nested validation to check if two other fields (one or the other, or implicitly both) exist inside of it. I am using Express Validator to try and accomplish this task.
// Sample request body
{
<...>
thresholds: {
min: 3,
max: 5
}
}
// (Attempted) validation chain
check('thresholds').optional()
.custom( innerBody => {
console.log('THRESHOLDS', innerBody);
oneOf([
check('innerBody.min').optional(),
check('innerBody.max').optional()
]);
})
The above snippet is part of a larger validation chain I'm validating the full request body on. I also tried removing the innerBody. string from the inner checks but still no luck. I am console.loging the threshold body, and it prints out correctly, however I still get a validation error, when I'm trying to get my integration test to pass:
{"name":"ValidationError","message":"ValidationError: Validation failed","errors":[{"location":"body","param":"thresholds","value":{"min":3,"max":5},"msg":"Invalid value"}]}
I am relatively new to Express Validator so if I'm chaining the validation wrong/not using oneOf correctly or something would love some pointers!
Thanks
Looks like the .custom function needs to return a Promise. Answer below:
.custom(innerBody => {
if (!(innerBody.min) || !(innerBody.max)) return Promise.reject('Missing min or max');
return Promise.resolve();
})
Remember: Always return a boolean value from the callback of .custom()
function. Otherwise your validation might not work as desired.
Source: Custom validation guide
In general, you might have needs in use of Promises if you deal with asynchronous .custom() function. Then you'll be obligated to return Promise.resolve() / Promise.reject() for correct validator behaviour.
Source: SO answer

Passing parameters to db.query with arangojs

I'm having problems sending parameters with the ArangoJS library and was wondering if anyone could help.
With the example below, it is possible to execute db.query if parameter values are in the query, but as soon as I try to use bindVars I get silent errors and I can't extract any error details.
var db = require('arangojs')("http://127.0.0.1:8529");
/*
The '_system' database contains a collection called 'test' that contains one document:
{
"a": 1,
"b": 2
}
*/
// This works
db.query('FOR t IN test FILTER t.a == 1 RETURN t')
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nNo bindVars");
console.log(vals);
});
});
// This does not work
db.query("FOR t IN #first FILTER t.a == #second RETURN t", { first: "test", second: 1 })
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nUsing bindVars");
console.log(vals);
});
});
I'm new to Node.js and ArangoDB and would love to be able to use properly parameterized queries.
I'm also assuming that this use of parameters protects you from SQL Injection style attacks?
Thanks!
The problem isn't with the JavaScript driver or Node, the problem is with the query itself:
FOR t IN #first FILTER t.a == #second RETURN t
In AQL collection names can't be injected with ordinary bind parameters. This is because you're not actually trying to use the parameter as a string value but to refer to a collection with that name. To quote the AQL documentation:
A special type of bind parameter exists for injecting collection names. This type of bind parameter has a name prefixed with an additional # symbol (thus when using the bind parameter in a query, two # symbols must be used).
In other words, in AQL it has to be called ##first (instead of #first) and in the bind parameters argument to db.query it has to be called #first (instead of just first).
When using arangojs it's actually possible to avoid this entirely by using the aqlQuery template handler:
var aqlQuery = require('arangojs').aqlQuery;
var first = db.collection('test');
var second = 1;
db.query(aqlQuery`
FOR t IN ${first}
FILTER t.a == ${second}
RETURN t
`).then(
cursor => cursor.all()
).then(vals => {
console.log('Using aqlQuery');
console.log(vals);
});
This way you don't have to think about bind parameter syntax when writing queries and can write more complex queries without having to mess with extremely long strings. Note that it will recognize arangojs collection instances and handle them accordingly. Using a string instead of a collection instance would result in the same problems as in your example.
Additionally note that the template handler also exists in the arangosh shell and in ArangoDB itself (e.g. when using Foxx).

Check for 'http://' text in json object with Chai.js Should

I've got some nested objects inside each object and I want to check with chai that the images's hrefs start with 'http://'
{
"images": [
{
"header": {
"href": "http://somedomain.com/assets/header.jpg"
}
},
{
"logo": {
"href": "http://somedomain.com/assets/logo.jpg"
}
}
]
}
The problem is, I can't just key off of the image name property because it changes...so I can't do this:
images[0].[image name changes!! it's not a concrete property name].href.should.have.deep.property("href");
because imagename is like 'header', 'logo', and so on
so how would I be able to do this with chai and check the href for each image to make sure it's got the text 'http://'?
You could dynamically iterate through all objects in the images array as #gfpacheco suggested in his answer.
But I would research a way to create a deterministic test. This would simplify your assertions, but might require some creativity or refactoring to mock or render fixtures
I tried to write the entire Chai assertion function. It uses the Chai Things plugin to work with assertions over arrays:
var data = {...};
function startsWithHttp(string) {
return string.indexOf('http://') === 0;
}
data.images.should.all.satisfy(function(image) {
Object.keys(image).should.contain.an.item.that.satisfy(function(key) {
image[key].should.have.all.keys('href');
image[key].href.should.satisfy(startsWithHttp);
});
});
It assumes that all images should have at least one property that it's value must have an href property and it's value starts with http://.
As I said #dm03514, it's hard to find a deterministic method. I get the sub string of href and would verify if is equals (and iterate how #gfpacheco did do). I tried let the same environment for their:
var http = "http://";
if(header.href.substr(0,7) === http)
alert("Header ok");
else
alert("Header no ok")
https://jsfiddle.net/lbclucascosta/zhrfLa8m/2/
Obs: This is pure javascript, but you can get the substring in node too. nodejs: string manipulation

express.js - single routing handler for multiple routes in a single line

Is there a way to make this on a single function call?
var todo = function (req, res){};
app.get("/", todo);
app.get("/blabla", todo);
app.get("/blablablabla", todo);
Something like:
app.get("/", "/blabla", "/blablablabla", todo );
I know this is a syntax mess, but just for giving an idea of what I would like to achieve, an array of the routes would be awesome!
Anyone know how to do this?
I came across this question while looking for the same functionality.
#Jonathan Ong mentioned in a comment above that using arrays for paths is deprecated but it is explicitly described in Express 4, and it works in Express 3.x. Here's an example of something to try:
app.get(
['/test', '/alternative', '/barcus*', '/farcus/:farcus/', '/hoop(|la|lapoo|lul)/poo'],
function ( request, response ) {
}
);
From inside the request object, with a path of /hooplul/poo?bandle=froo&bandle=pee&bof=blarg:
"route": {
"keys": [
{
"optional": false,
"name": "farcus"
}
],
"callbacks": [
null
],
"params": [
null,
null,
"lul"
],
"regexp": {},
"path": [
"/test",
"/alternative",
"/barcus*",
"/farcus/:farcus/",
"/hoop(|la|lapoo|lul)/poo"
],
"method": "get"
},
Note what happens with params: It is aware of the capture groups and params in all of the possible paths, whether or not they are used in the current request.
So stacking multiple paths via an array can be done easily, but the side-effects are possibly unpredictable if you're hoping to pick up anything useful from the path that was used by way of params or capture groups. It's probably more useful for redundancy/aliasing, in which case it'll work very well.
Edit: Please also see #c24w's answer below.
Edit 2: This is a moderately popular answer. Please keep in mind that ExpressJS, as with most Node.js libraries, is a moveable feast. While the routing above does still work (I'm using it at the moment, a very handy feature), I cannot vouch for the output of the request object (it's certainly different from what I've described). Please test carefully to ensure you get the desired results.
app.get('/:var(bla|blabla)?', todo)
:var sets the req.param that you don't use. it's only used in this case to set the regex.
(bla|blabla) sets the regex to match, so it matches the strings bla and blablah.
? makes the entire regex optional, so it matches / as well.
You can actually pass in an array of paths, just like you mentioned, and it works great:
var a = ['/', '/blabla', '/blablablabla'];
app.get(a, todo);
Just to elaborate on Kevin's answer, this is from the 4.x docs:
The path for which the middleware function is invoked; can be any of:
A string representing a path.
A path pattern.
A regular expression pattern to match paths.
An array of combinations of any of the above.
They have some examples, including:
This will match paths starting with /abcd, /xyza, /lmn, and /pqr:
app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], function (req, res, next) {
next();
});
I went for a:
['path', 'altPath'].forEach(function(path) {
app.get(path, function(req, res) { etc. });
});

Undefined attribute in jade template engine

I'm simply trying to display a value in an input field with Jade (0.20.3) and Express (2.5.8):
input(name='username', type='text', id="username", value=username)
It is quite simple, but this throws an error when the value is undefined:
username is not defined
However, the documentation indicates:
When a value is undefined or null the attribute is not added, so this is fine, it will not compile 'something="null"'.
Is there something that I would have done wrong?
Short answer: use locals.someVar if you're not sure that someVar exists.
Longer Answer:
I think that Brandon's initial answer and last comment are correct (though the #{...} syntax isn't needed), but to elaborate a bit: There's a difference between passing in an a variable (technically, an object property) with a value of undefined, and not passing that variable at all.
Because Jade transforms your template into JS source and evals it (in the context of a with block), you have to make sure that you're not referring to any variables that haven't been passed in, or they'll be . This blog post has some background on undefined vs undeclared variables and ReferenceError.
Your Jade template should work correctly if you do one of these things:
// ok, even if req.session.username is not defined
res.render('index', { username: req.session.username })
// ditto
res.local('username', req.session.username);
res.render('index')
But these won't work:
res.locals(req.session) //if no username property exists
res.render('index', { /* no username */ } )
If it's not practical to manually pass in each parameter you might want to refer to, you can refer to the variable in your template as properties on the locals object (e.g. locals.username)
You have to make sure you're passing username into the Jade template. Example:
app.get('/', function (req, res) {
res.render('index', { title:'APP NAME', username: req.session.username });
});
Also, you would call it like this in the Jade template: #{username}

Resources