A Hello World Example

The client API or browser sends a HTTP request to the ArangoDB server and theserver returns a HTTP response to the client. A HTTP request consists of amethod, normally GET or POST when using a browser, and a request path like/hello/world. For a real Web server there are a zillion of other thing toconsider, we will ignore this for the moment. The HTTP response contains acontent type, describing how to interpret the returned data, and the dataitself.

In the following example, we want to define an action in ArangoDB, so that theserver returns the HTML document

  1. <html>
  2. <body>
  3. Hello World
  4. </body>
  5. </html>

if asked GET /hello/world.

The server needs to know what function to call or what document to deliver if itreceives a request. This is called routing. All the routing information ofArangoDB is stored in a collection _routing. Each entry in this collectionsdescribes how to deal with a particular request path.

For the above example, add the following document to the _routing collection:

arangosh> db._routing.save({ 
........>  url: { 
........>    match: "/hello/world" 
........>  },
........>  content: { 
........>    contentType: "text/html", 
........>    body: "<html><body>Hello World</body></html>" 
........>  }
........> });

Show execution results

{ 
  "_id" : "_routing/67098", 
  "_key" : "67098", 
  "_rev" : "_ZP4PG-----" 
}

Hide execution results

In order to activate the new routing, you must either restart the server or callthe internal reload function.

arangosh> require("internal").reloadRouting()

Show execution results





Hide execution results

Now use the browser and access http:// localhost:8529/hello/world

You should see the Hello World in our browser:





Show execution results

shell> curl --header 'accept: application/json' --dump - http://localhost:8529/hello/world

HTTP/1.1 OK
content-type: text/html
x-content-type-options: nosniff

"Hello World"

Hide execution results

Matching a URL

There are a lot of options for the url attribute. If you define differentrouting for the same path, then the following simple rule is applied in order todetermine which match wins: If there are two matches, then the more specificwins. I. e, if there is a wildcard match and an exact match, the exact match ispreferred. If there is a short and a long match, the longer match wins.

Exact Match

If the definition is

{ 
  url: { 
    match: "/hello/world" 
  } 
}

then the match must be exact. Only the request for /hello/world will match,everything else, e. g. /hello/world/my or /hello/world2, will not match.

The following definition is a short-cut for an exact match.

{ 
  url: "/hello/world" 
}

Note: While the two definitions will result in the same URLmatching, there is a subtle difference between them:

The former definition (defining url as an object with a match attribute)will result in the URL being accessible via all supported HTTP methods (e.g.GET, POST, PUT, DELETE, …), whereas the latter definition (providing a stringurl attribute) will result in the URL being accessible via HTTP GET and HTTP HEAD only, with all other HTTP methods being disabled. Calling a URLwith an unsupported or disabled HTTP method will result in an HTTP 501 (not implemented) error.

Prefix Match

If the definition is

{ 
  url: { 
    match: "/hello/world/*" 
  } 
}

then the match can be a prefix match. The requests for /hello/world,/hello/world/my, and /hello/world/how/are/you will all match. However/hello/world2 does not match. Prefix matches within a URL part,i. e. /hello/world*, are not allowed. The wildcard must occur at the end,i. e.

/hello/*/world 

is also disallowed.

If you define two routes

{ url: { match: "/hello/world/*" } }
{ url: { match: "/hello/world/emil" } }

then the second route will be used for /hello/world/emil because it is morespecific.

Parameterized Match

A parameterized match is similar to a prefix match, but the parameters are alsoallowed inside the URL path.

If the definition is

{ 
  url: { 
    match: "/hello/:name/world" 
  } 
}

then the URL must have three parts, the first part being hello and the thirdpart world. For example, /hello/emil/world will match, while/hello/emil/meyer/world will not.

Constraint Match

A constraint match is similar to a parameterized match, but the parameters cancarry constraints.

If the definition is

{ 
  url: { 
    match: "/hello/:name/world", 
    constraint: { 
      name: "/[a-z]+/" 
    } 
  }
}

then the URL must have three parts, the first part being hello and the thirdpart world. The second part must be all lowercase.

It is possible to use more then one constraint for the same URL part.

{ 
  url: { 
    match: "/hello/:name|:id/world",
    constraint: { 
      name: "/[a-z]+/", id: "/[0-9]+/" 
    } 
  }
}

Optional Match

An optional match is similar to a parameterized match, but the last parameter isoptional.

If the definition is

{ 
  url: { 
    match: "/hello/:name?", 
    constraint: { 
      name: "/[a-z]+/"
    } 
  }
}

then the URL /hello and /hello/emil will match.

If the definitions are

{ url: { match: "/hello/world" } }
{ url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } }
{ url: { match: "/hello/*" } }

then the URL /hello/world will be matched by the first route, because it isthe most specific. The URL /hello/you will be matched by the second route,because it is more specific than the prefix match.

Method Restriction

You can restrict the match to specific HTTP methods.

If the definition is

{ 
  url: { 
    match: "/hello/world", 
    methods: [ "post", "put" ] 
  }
}

then only HTTP POST and PUT requests will match.Calling with a different HTTP method will result in an HTTP 501 error.

Please note that if url is defined as a simple string, then only theHTTP methods GET and HEAD will be allowed, an all other methods will bedisabled:

{ 
  url: "/hello/world" 
}

More on Matching

Remember that the more specific match wins.

  • A match without parameter or wildcard is more specific than a match withparameters or wildcard.
  • A match with parameter is more specific than a match with a wildcard.
  • If there is more than one parameter, specificity is applied from left toright.Consider the following definitions
arangosh> db._routing.save({ 
........>  url: { match: "/hello/world" },
........> content: { contentType: "text/plain", body: "Match No 1"} });
arangosh> db._routing.save({ 
........>  url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } },
........> content: { contentType: "text/plain", body: "Match No 2"} });
arangosh> db._routing.save({ 
........>  url: { match: "/:something/world" },
........> content: { contentType: "text/plain", body: "Match No 3"} });
arangosh> db._routing.save({ 
........>  url: { match: "/hi/*" },
........> content: { contentType: "text/plain", body: "Match No 4"} });
arangosh> require("internal").reloadRouting()

Show execution results

{ 
  "_id" : "_routing/67108", 
  "_key" : "67108", 
  "_rev" : "_ZP4PGAy---" 
}
{ 
  "_id" : "_routing/67110", 
  "_key" : "67110", 
  "_rev" : "_ZP4PGA2---" 
}
{ 
  "_id" : "_routing/67112", 
  "_key" : "67112", 
  "_rev" : "_ZP4PGA6---" 
}
{ 
  "_id" : "_routing/67114", 
  "_key" : "67114", 
  "_rev" : "_ZP4PGA6--A" 
}

Hide execution results

Then





Show execution results

shell> curl --header 'accept: application/json' --dump - http://localhost:8529/hello/world

HTTP/1.1 OK
content-type: text/plain
x-content-type-options: nosniff

"Match No 1"
shell> curl --header 'accept: application/json' --dump - http://localhost:8529/hello/emil

HTTP/1.1 OK
content-type: text/plain
x-content-type-options: nosniff

"Match No 2"
shell> curl --header 'accept: application/json' --dump - http://localhost:8529/your/world

HTTP/1.1 OK
content-type: text/plain
x-content-type-options: nosniff

"Match No 3"
shell> curl --header 'accept: application/json' --dump - http://localhost:8529/hi/you

HTTP/1.1 OK
content-type: text/plain
x-content-type-options: nosniff

"Match No 4"

Hide execution results

You can write the following document into the _routing collectionto test the above examples.

{
  routes: [
{ url: { match: "/hello/world" }, content: "route 1" },
{ url: { match: "/hello/:name|:id", constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }, content: "route 2" },
{ url: { match: "/:something/world" }, content: "route 3" },
{ url: { match: "/hello/*" }, content: "route 4" },
  ]
}