HTTP Route Patterns

A little background

Since we first introduced support for RESTful Services back in ORDS (neĆ© APEX Listener) 1.1 (around October 2011) we’ve used URI Templates as the syntax to define the URI patterns that a RESTful Service would respond to. In fact the URI Template syntax we use is based on an early draft of the URI Template specification (the URI Template spec wasn’t finalized until March 2012), and as a consequence lacks much of the richness of the final specification.

Then there’s the fact that URI Templates are focussed on forming concrete URIs from templates. Not so much focussed on matching concrete URIs against templates and decomposing them into parameters.

This results in a couple of pain points:

  • Template parameters are greedier than folks expect. Given a template like:

    hr/employees/{id}
    

    Most folks expect this URI Template to match a URI like the following:

    https://example.com/ords/schema/hr/employees/101
    

    but they don’t expect it to match a URI like the following:

    https://example.com/ords/schema/hr/employees/101/details
    

    Folks don’t realize that {id} roughly translates into a (.*) regex, and that path separators have no specical signficance. This leads to all kinds of confusion, especially if a URI template has more than one parameter.

  • No way to express optional parameters. URI Templates match against the entire path & query string of a URI. There’s no way to express that a parameter may be optional, the only workaround is to define two URI Templates, one with the parameter, one without. If you have more than one optional parameter, this quickly becomes painful. Worse, the position of the parameters is fixed, so the ordering of query string based parameters becomes unecessarily strict.

We wanted to address these pain points, but do so in a compatible manner. To that end we’ve introduced a new syntax for URI patterns. We still fully support the existing URI Template based syntax, but going forward we’ll be encouraging everyone to switch to the new syntax, we believe the change will be worth it.

Introducing Route Patterns

We needed a new syntax that was distinct from the existing URI Template based syntax, but we didn’t want to magic up some obscure new syntax. So we picked something that is going to be familiar to a lot of folks. If you’ve used Ruby on Rails or Angular (and lots of other web frameworks), you are going to feel right at home, here’s a couple of example route patterns:

/hr/employees/:id
/hr/employees/:dept/

Syntax Overview

Parameter Names

As you can tell the old curly brace ({param-name}) syntax has been replaced by the new colon based syntax (:param-name). This matches the syntax commonly used by many web frameworks. Parameter names can include any characters in the range [a-z],[A-Z],[0-9] and the following characters: .,-,_. The parameter name must start with an alphabetic character. The end of a parameter name is indicated by the / character or the end of the string.

Path Separators are significant

In constrast to the URI Template based syntax, path separators (the / character) are significant. We call the text falling between two path separators a path segment. A parameter will only match against a single path segment 1. This makes route patterns more intuitive and easier to reason about (It also makes things much simpler for ORDS, enabling it to more accurately choose the best match for a URI).

For example, the following route pattern:

/hr/employees/:id

will match:

https://example.com/ords/some-schema/hr/employees/101
https://example.com/ords/some-schema/hr/employees/joe,bloggs

but will not match:

https://example.com/ords/some-schema/hr/employees/101/detail
https://example.com/ords/some-schema/hr/employees/joe/bloggs

Only the path section of the URI is matched

The existing URI Template based syntax matches against the path and query string portions of the URI. The Route Pattern syntax matches against the path portion only. It doesn’t matter what appears in a URI’s query string, or even matter if there is a query string at all, it will have no bearing whatsoever on what route pattern is matched to the URI. In fact it is illegal to have the query string delimiter (?) in a route pattern.

This leaves the query string parameters available to be used to transport optional parameters.

Optional Parameters

Optional parameters are never declared in the route pattern. Instead any query string parameter is an optional parameter. You can refer to optional parameters in the source of a resource handler by using a bind variable with the same name.

Say I want to be able to filter a list of employees by department. This is my route pattern:

/hr/employees/

and the source for my GET resource handler is:

1
select * from emp

I’d like to have an optional parameter named dept that would let me filter by department id, so the source for my resource handler would become:

1
select * from emp where deptno = nvl(:dept,deptno)
  • If the :dept bind value is null, then select every row where deptno equals itself, i.e. select every row.
  • If the :dept bind value is not null, then select every row with a matching deptno column.

Well, thanks to the syntax of route patterns, I can just edit my resource handler’s source to match the above and it will automatically support the passing of the optional dept parameter. If the service is invoked as follows:

Host: example.com
GET /ords/some-schema/hr/employees/
  • :dept is bound to the null value, so all rows are selected.

then a response showing all employees will be returned. If I add the dept query parameter then the response will be filtered to only show employees from that department, e.g.:

Host: example.com
GET /ords/some-schema/hr/employees/?dept=30
  • :dept is bound to the value: 30. Only rows where deptno = 30 will be returned.

More detail

The full specification of the Route Patterns syntax is available here.

Footnotes


  1. A parameter may match multiple path segments if it is annotated with the * modifier, see the Route Patterns Specification ↩︎

Ⓗ Home   Ⓑ Blog   Ⓐ About