Oracle REST Data Services 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:
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:
select * from emp where deptno = nvl(:dept,deptno)
- If the
:dept
bind value is null, then select every row wheredeptno
equals itself, i.e. select every row. - If the
:dept
bind value is not null, then select every row with a matchingdeptno
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 thenull
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 wheredeptno = 30
will be returned.
More detail
The full specification of the Route Patterns syntax is available here.
Footnotes
-
A parameter may match multiple path segments if it is annotated with the
*
modifier, see the Route Patterns Specification ↩