0

I have this so far. It works, but I can't help but wonder if there something less hacky.

GET request:

http://localhost:3100/api/things?matches=%7B%22name%22%3A%22asdf%22%7D

decoded: matches={"name":"asdf"} -- I basically take the data object for a GET request, make the keys the query string parameter names and the JSON.stringify value as the query string value.

It works....but I feel like maybe I can do it smoother with an endpoint like:

GET http://localhost:3100/api/things/match/:attr/:value -- but then it's very limiting as I can only have one condition. Whereas passing the whole object above I can match on multiple attributes.

On the Koa side of things, it's not too much extra code (I'm using Thinky for RethinkDB):

  /**
   * list
   * list all things
   * @param next
   */
  ctrl.list = function *(next){
    var matches = this.request.query.matches && JSON.parse(this.request.query.matches);

    if ( matches ) {
      var result = yield Thing.orderBy({index: "createdAt"}).filter(function(doc){
        return doc('name').match(matches.name);
      });
    } else {
      var result = yield Thing.orderBy({index: "createdAt"});
    }

    this.body = result;

    yield next;
  };

If there is no query string, then it just returns all results.

Am I on the right track here?

chovy
  • 72,281
  • 52
  • 227
  • 295
  • I don't know much koa, just a comment on your choice of endpoint: If you use `/:attr/:value` wouldn't you be passing multiple query terms like this: `../matches/name/asdf/color/red/prop/value...` ?? Seems.. awkward. Wouldn't it be better to parse them as query strings? `../matches?name=asdf&color=red&prop=val` – laggingreflex Jun 12 '15 at 05:24
  • the idea with the query string approach is I could have multiple filter objects passed like `matches={name: 'asdf'}&where={ age: 18 }` etc. etc. – chovy Jun 12 '15 at 08:44
  • It seems that, while hacky, your solution is probably the most flexible one. Do you just want to have an HTTP layer over your models, or will the interface be more limited? – Jorge Silva Jun 12 '15 at 17:44
  • HTTP and potentially sockets. – chovy Jun 12 '15 at 23:25

1 Answers1

2

I believe this is the correct way. This approach is used by Baucis, and also koa-mongo-rest with Mongoose. It can be even taken a bit further. An example url like:

/api/users?conditions={"name":"john", "city":"London"}&limit=10&skip=1&sort=-zipcode

Can be processed by following code:

findAll: function*(next) {
  yield next;
  var error, result;
  try {
    var conditions = {};
    var query = this.request.query;
    if (query.conditions) {
      conditions = JSON.parse(query.conditions);
    }
    var builder = model.find(conditions);
    ['limit', 'skip', 'sort'].forEach(function(key){
      if (query[key]) {
        builder[key](query[key]);
      }
    })
    result = yield builder.exec();
    return this.body = result;
  } catch (_error) {
    error = _error;
    return this.body = error;
  }
}

I'd appreciate to have a similar library for RethinkDB as well.

Tomas Holas
  • 886
  • 10
  • 20
  • ok but think about if you need to go further down and specify a property of name. Lets say name was actually an object and it had properties like full, nick, etc. How would you start specifying lower hierarchy chain in a resource that has properties and in one of those properties you wanna get to it's property and so on to set as the criteria. I can see this only working at the same hierarchy level of the resource you are requesting so I don't see it handling more complex cases. – PositiveGuy Oct 15 '15 at 20:24
  • Couldn't you just use dot notation? (see http://stackoverflow.com/questions/12842632/mongodb-query-slow-with-dot-notation) – Tomas Holas Oct 18 '15 at 10:47