12

I am using node and express. To register a controller I call:

app.get('/user/:id', function (req, res) {...});  

But I would like to do it the rfc-6570 way:

app.get('/user/{id}', function (req, res) {...});

I googled just an implementation in python on google code, but found nothing (except the dead link on google code to http://www.snellspace.com/wp/?p=831) for JavaScript.

URI templating in general is not so easy as it looks on the first sight. Have a look on the examples in the RFC.

PS: I will need the URI templates on the client, too.

ᄂ ᄀ
  • 5,214
  • 6
  • 38
  • 54
afx
  • 763
  • 1
  • 7
  • 10
  • That RFC is only two months old; I wouldn't be surprised if there aren't any compliant implementations for awhile... – maerics May 09 '12 at 15:15
  • Out of curiosity, why do you want to use that RFC instead of the form that Express provides? – maerics May 09 '12 at 15:27
  • First, the rfc is only 2 months old. But the drafts are about 3 years old. – afx May 10 '12 at 19:45
  • Second, I want to use the RFC way, because I have a mixed environment on the server: express and java/spring. Spring is using the rfc style (closely). I want to use the same templates with spring and express -- and in the user agent, of course. The rfc way is much more expressive than the express/(ruby?) way – afx May 10 '12 at 19:51
  • and finally Third, if there is no implementation, I will create one ;-) – afx May 10 '12 at 19:53
  • AFAICT, RFC 6570 is for parsing templates as a Consumer, not for specifying a syntax accepted by a Provider. It goes from URI Template + Variables to URL, not the other way around. – Thomas Hunter II Jan 27 '14 at 22:42
  • please see http://stackoverflow.com/questions/19919123/can-uri-templates-be-used-to-match-uris-to-routes – sebilasse Apr 01 '16 at 08:42
  • @afx 4.5yrs in, can we still count on your library? :) – Danosaure Apr 05 '17 at 15:45

3 Answers3

7

I've been cleaning up the implementations list at http://code.google.com/p/uri-templates/wiki/Implementations - there is a JS one at https://github.com/marc-portier/uri-templates but I'm not sure of whether it implements the RFC, nor of what its quality is.

Note that we've started publishing tests here: https://github.com/uri-templates/uritemplate-test

So if you want to check it, you could start there.

mb21
  • 28,026
  • 6
  • 96
  • 118
Mark Nottingham
  • 4,451
  • 1
  • 21
  • 20
  • Thank you! I will integrate the tests when implementing. The work of marc portier is fine, but it can only parse a template and expand it to an URI. But the difficult part is to extract the variables of a given URI with a uriTemplate. As You wrote in the last paragraph of 1.5 in the rfc: "Some URI Templates can be used in reverse for the purpose of variable matching: comparing the template to a fully formed URI in order to extract the variable parts from that URI and assign them to the named variables." – afx May 26 '12 at 16:46
  • @afx Have you implement it somewhere? I'd like to do similar thing and will have to create one too, anywhere I can join? – Almad Jul 31 '12 at 12:07
  • @afx Sorry, somehow missed that you are probably fxa from github ;) – Almad Jul 31 '12 at 13:46
  • Thank you @afx for developing this library! :) – Hendy Irawan Oct 21 '12 at 08:58
4

As of June 2014, these JavaScript implementations seem most complete (Level 4 of the spec) and tested. All three also support both the browser and node.js.

mb21
  • 28,026
  • 6
  • 96
  • 118
0

Regarding the express router part I would recommend to use your uri templates within a hyperschema (read more) ...

Then you could also benefit from regex for your router which express.js supports. Regarding resolving the parameters you need an RFC 6570 implementation like https://github.com/geraintluff/uri-templates ...

Here is some .js code to illustrate the rewriting of a hyperschema USING RFC 6570 to convert it to an express js router:

  var hyperschema = {
  "$schema": "http://json-schema.org/draft-04/hyper-schema",
  "links": [
    {
      "href": "{/id}{/ooo*}{#q}",
      "method": "GET",
      "rel": "self",
      "schema": {
        "type": "object",
        "properties": {
          "params": {
            "type": "object",
            "properties": {
              "id": {"$ref": "#/definitions/id"}
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": true
      }
    }
  ],
  "definitions": {
    "id": {
      "type": "string",
      "pattern": "[a-z]{0,3}"
    }
  }
}
  var deref = require('json-schema-deref');
  var tv4 = require('tv4');
  var url = require('url');
  var rql = require('rql/parser');

// DOJO lang AND _
function getDottedProperty(object, parts, create) {
    var key;
    var i = 0;

    while (object && (key = parts[i++])) {
        if (typeof object !== 'object') {
            return undefined;
        }
        object = key in object ? object[key] : (create ? object[key] = {} : undefined);
    }

    return object;
}
function getProperty(object, propertyName, create) {
    return getDottedProperty(object, propertyName.split('.'), create);
}
function _rEscape(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function getPattern(k, ldo, customCat) {
  // ...* = explode = array
  // ...: = maxLength
  var key = ((k.slice(-1) === '*') ? k.slice(0,-1) : k).split(':')[0];
  var cat = (customCat) ? customCat : 'params'; // becomes default of customCat in TS
  var pattern = '';
  if (typeof ldo === 'object' && ldo.hasOwnProperty('schema')) {
    var res = getProperty(ldo.schema, ['properties',cat,'properties',key,'pattern'].join('.'));
    if (res) {
      console.log(['properties',cat,'properties',key,'pattern'].join('.'),res);
      return ['(',res,')'].join('');
    }
  }
  return pattern;
}
function ldoToRouter(ldo) {
  var expression = ldo.href.replace(/(\{\+)/g, '{') // encoding
    .replace(/(\{\?.*\})/g, '') // query
    .replace(/\{[#]([^}]*)\}/g, function(_, arg) {
      // crosshatch
      //console.log(arg);
      return ['(?:[/]*)?#:',arg,getPattern(arg,ldo,'anchor')].join('');
    })
    .replace(/\{([./])?([^}]*)\}/g, function(_, op, arg) {
      // path seperator
      //console.log(op, '::', arg, '::', ldo.schema);
      return [op,':',arg,getPattern(arg,ldo)].join('');
    });
    return {method: ldo.method.toLowerCase(), args:[expression]};
}

deref(hyperschema, function(err, fullSchema) {
  console.log('deref hyperschema:',JSON.stringify(fullSchema));
  var router = fullSchema.links.map(ldoToRouter);

  console.log('router:',JSON.stringify(router));
});
sebilasse
  • 3,202
  • 2
  • 30
  • 30