7

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!

David Thomas
  • 1,894
  • 2
  • 14
  • 18

1 Answers1

4

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).

Alan Plum
  • 10,627
  • 4
  • 37
  • 53
  • Thank you very much for that @Alan, it's working for me now. It helps explain the different ways to query ArangoDb, which I'm loving by the way. Thanks again. – David Thomas Jan 07 '16 at 11:40
  • Just a quick follow-up on the SQL Injection Attack protection part of the question, would it be better to stick to standard db.query based parameters for input from a user, as it would be possible for an end user to inject code into a template string? – David Thomas Jan 07 '16 at 11:56
  • @DavidThomas, the template handler actually inserts variable names and produces a query and a bind object mapping the names to the parameters. The result is identical to doing it manually. Injection attacks can only occur if you build the strings manually (by concatenation or using raw template strings without the aqlQuery template handler). – Alan Plum Jan 07 '16 at 12:20