- Requesting a resource
- Handling the response
- Building a resource
- Listening to a stream resource
- Building a stream resource
- Requesting DOM view resource
- Building DOM view resource
- Top level navigation
To request a resource, you will need to import the address module with the modules:
// using ES6 modules
import { address } from '@zambezi/address'
// using AMD modules
define([
'@zambezi/address/lib/address'
], function(address) {
// ...
})
Creates a resource request object, with an optional uri parameter, that is used to:
- provide additional request options - headers and accept types
- specify data with the request - parameters, query strings, body
- register listeners to response events
- initiate the request once all configuration has been done
address('/price/usd/gbp')
.on('ok', function(response) {
// use data from response
})
.get()
Provides set or get of the resource uri to be requested.
set:
address()
.uri('/price/usd/gbp')
.on('ok', function(response) {
// use data from response
})
.get()
get:
var value = address()
.uri('/price/usd/gbp')
.uri()
// value === '/price/usd/gbp'
Provides set or get for path parameters, using key-value arguments or an object of key-value properties, which are interpolated into the path.
set (key-value arguments):
address('/price/{ccy1}/{ccy2}')
.param('ccy1', 'usd')
.param('ccy2', 'gbp')
.get()
// -> /price/usd/gbp
set (object of key-value properties):
address('/price/{ccy1}/{ccy2}')
.param({ ccy1: 'usd', ccy2: 'gbp' })
.get()
// -> /price/usd/gbp
get (object of key-value properties):
var value = address('/price/{ccy1}/{ccy2}')
.param('ccy1', 'usd')
.param('ccy2', 'gbp')
.param()
// value === { ccy1: 'usd', ccy2: 'gbp' }
Provides set or get for query parameters, using key-value arguments or an object of key-value properties, which will be appended to the path.
set (key-value arguments):
address('/price/usd/gbp')
.query('ccy1', 'usd')
.query('ccy2', 'gbp')
// -> /price/usd/gbp?ccy1=usd&ccy2=gbp
set (object of key-value properties):
address('/price/usd/gbp')
.query({ ccy1: 'usd', ccy2: 'gbp' })
// -> /price/usd/gbp?ccy1=usd&ccy2=gbp
get (object of key-value properties):
var value = address('/price/usd/gbp')
.query('ccy1', 'usd')
.query('ccy2', 'gbp')
.query()
// value === { ccy1: 'usd', ccy2: 'gbp' }
Provides set or get for the body of your request. This can be any object and will be accessible to the resource through the request.body
property.
set:
address('/price/usd/gbp')
.body({ foo: 'bar' })
.post()
get:
var value = address('/price/usd/gbp')
.body({ foo: 'bar' })
// value === { foo: 'bar' }
To disambiguate the DOM element used as the local root, when navigating or using DOM view resource, it is possible to specify an origin node when building a request.
The origin
node provided should be a descendant of the root node in which the request should be resolved. In cases where the running application contains more than one possible root (for instance when running in browser workspaces), specifying an origin node allows address to navigate in the correct window.
example: navigation with origin specified
address('/price/usd/gbp')
.origin(node)
.navigate()
example: using DOM view resource with origin specified
address('/price/usd/gbp')
.origin(node)
.into()
.get()
Invoke the request using the get method.
address('/price/usd/gbp')
.on('ok', function(response) {
// use data from response
})
.get()
Invoke the request using the post method.
A payload can be passed in as a parameter, which will be equivalent to passing in a payload through .body()
.
address('/price/usd/gbp')
.post({ foo: 'bar' })
Invoke the request using the put method.
A payload can be passed in as a parameter, which will be equivalent to passing in a payload through .body()
.
address('/price/usd/gbp')
.put({ foo: 'bar' })
Invoke the request using the remove method.
A payload can be passed in as a parameter, which will be equivalent to passing in a payload through .body()
.
address('/price/usd/gbp')
.remove({ foo: 'bar' })
Invoke the request using the patch method.
A payload can be passed in as a parameter, which will be equivalent to passing in a payload through .body()
.
address('/price/usd/gbp')
.patch({ foo: 'bar' })
Set method for the request, with invoking the request.
The method chosen should match the semantics found in the standard HTTP verbs):
GET
POST
PUT
REMOVE
PATCH
address('/price/usd/gbp')
.method('post')
.body(someData)
() // invoke request
Allows headers to be added to a request, so that further information can be conveyed about how the request should be handled.
For example, resources may route requests to different handlers based on the accept type
of the request.
Specify the accept type
header using the .header()
api.
address('/price/usd/gbp')
.header('accept-type', 'application/json')
.on('ok', function(response) {
// use data from response
})
.get()
Accept types must conform to the HTTP field definition
The default accept type for requests is 'application/x.nap.view' which is the type for a view resource.
A convenience methods for setting the accept type header to 'application/json'
:
address('/price/usd/gbp')
.json()
.on('ok', function(response) {
// use data from response
})
.get()
A convenience methods for setting the accept type header to 'text/xml'
:
address('/price/usd/gbp')
.xml()
.on('ok', function(response) {
// use data from response
})
.get()
A convenience methods for setting the accept type header to 'text/plain'
:
address('/price/usd/gbp')
.text()
.on('ok', function(response) {
// use data from response
})
.get()
Listen for any response type as specified in the HTTP/1.1 specification:
listen for: successful
address('/price/usd/gbp')
.on('successful', function(response) {
// handle response
})
.get()
listen for: client-error
address('/price/usd/gbp')
.on('client-error', function(response) {
// handle response
})
.get()
Status Code Name | Status Code |
---|---|
'informational' |
1xx |
'successful' |
2xx |
'redirection' |
3xx |
'client-error' |
4xx |
'server-error' |
5xx |
Listen for specific status code, based open the corresponding names as defined in the specification:
listen for: created
address('/price/usd/gbp')
.on('created', function(response) {
// use data from response
})
.get()
listen for: bad-request
address('/price/usd/gbp')
.on('bad-request', function(response) {
// handle error response
})
.get()
Example Status Code Name | Status Code |
---|---|
'ok' |
200 |
'created' |
201 |
'bad-request' |
400 |
'unauthorized' |
401 |
'service-unavailable' |
503 |
Listen to when a request completes, regardless of status code:
address('/price/usd/gbp')
.on('done', function(response) {
// handle error response
})
.get()
Listen for the exception to be thrown during the request.
There are some errors that get sent from address:
- If the requested resource does not support the specified method a 405 error response will be returned.
- If the requested resource does not support the specified accept type a 415 error response will be returned.
address('/price/usd/gbp')
.on('err', function(response) {
// handle error response
})
.get()
To build a resource, some of the following exports might need to be imported:
// using ES6 modules
import { ok, error, created, redirect, response } from '@zambezi/address'
export function(req, res) {
// handle request and call `res` with response
}
// using AMD modules
define([
'@zambezi/address/lib/ok'
, '@zambezi/address/lib/error'
, '@zambezi/address/lib/created'
, '@zambezi/address/lib/redirect'
, '@zambezi/address/lib/response'
], function(ok, error, created, redirect, response) {
return function(req, res) {
// handle request and call `res` with response
}
})
The body payload associated with the request - see address.body()
:
var value = req.headers
// value === { foo: 'bar' }
An object of key-value properties for the headers associated with the request - see address.header()
:
var value = req.headers
// value === { accept: 'application/json' }
The method associated with the request - see address.method()
:
var value = req.method
// value === 'get'
The origin DOM node associated with the request - see address.origin()
:
var value = req.origin
// value === <div class="z-app"/>
An object of key-value properties for the params associated with the request - see address.param()
:
var value = req.params
// value === { ccy1: 'usd', ccy2: 'gbp' }
The uri associated with the request - see address(uri)
and address.uri()
:
var value = req.url
// value === '/price/usd/gbp'
The callback function, for passing back an error or a response object - see response(STATUS CODE, BODY, HEADERS)
.
An error should only be passed when there is a problem with making a request, where as when there is an error due to some response (such as a network request) should pass a response object.
example: with error
if (/* missing request parameter*/) {
res('Parameter "ccy1" has not been supplied.')
}
example: with an ok()
response object
res(null, ok({ amount: 10000, ccy1: 'usd', ccy2: 'gbp' }))
example: with an error()
response object
res(null, error(401, { message: 'User does not have permissions to make request' }))
A response object, which allows the status code, body and headers to be supplied.
A resource function must call the response function with an appropriate response.
res(null, response(201, { amount: 10000, ccy1: 'usd', ccy2: 'gbp' }, { 'location': 'order/1221' }))
A convenience method for a http status code 200 response object:
res(null, ok({ amount: 10000, ccy1: 'usd', ccy2: 'gbp' }))
A convenience method for a http status code 4xx or 5xx response object:
res(null, error(401, { message: 'User does not have permissions to make request' }))
A convenience method for a http status code 201 response object:
res(null, created('order/1221', { amount: 10000, ccy1: 'usd', ccy2: 'gbp' }))
A convenience method for a http status code 302 response object:
res(null, redirect('price/usd/gbp/now'))
A convenience methods for setting the accept type header to 'application/x.zap.stream'
:
var stream
address('/price/usd/gbp')
.stream()
.on('ok', function(response) {
stream = result.body
// handle stream object
})
.get()
Listen for errors sent on the stream:
var stream
address('/price/usd/gbp')
.stream()
.on('ok', function(response) {
stream = result.body
stream.on('error.custom-namespace', function (error) {
// Handle the error
})
})
.get()
Listen for messages sent on the stream:
var stream
address('/price/usd/gbp')
.stream()
.on('ok', function(response) {
stream = result.body
stream.on('message.custom-namespace', function (message) {
// Handle the message
})
})
.get()
Listen for status data sent on the stream:
var stream
address('/price/usd/gbp')
.stream()
.on('ok', function(response) {
stream = result.body
stream.on('status.custom-namespace', function (status) {
// Handle the status data
})
})
.get()
A stream resource is a resource with accept type application/x.zap.stream
.
Streams resources, when received, expose the on
method and can be listened to for different types of messages.
To build a resource, some of the following exports might need to be imported:
// using ES6 modules
import { stream } from '@zambezi/address'
// using AMD modules
define([
'@zambezi/address/lib/stream'
], function(ok, error, created, redirect, response) {
// ...
})
A factory method for producing a stream object.
return function handler(req, res) {
var resourceStream = stream()
// ...
res(null, ok(resourceStream))
}
Listen to when the first subscription has been made to the stream.
Typically a stream should not be performing any activity until there has been some subscription to the stream.
return function handler(req, res) {
var resourceStream = stream()
.on('firstsubscribed', function() {
// start up or connect to any network (or other) resources that you will want to stream
})
res(null, ok(resourceStream))
}
Listen to when the last subscription has been closed for the stream.
Typically a stream should clean up any connections or scheduled work when there are no longer any subscribers to the stream
return function handler(req, res) {
var resourceStream = stream()
.on('lastunsubscribed', function() {
// clean up any connections or scheduled work
})
res(null, ok(resourceStream))
}
A convenience method to send a message over the stream - see resourceStream.message()
:
return function handler(req, res) {
var resourceStream = stream()
// Send message over stream
resourceStream({ data: [1, 2, 3], id: 934 })
res(null, ok(resourceStream))
}
Sends an error over the stream:
return function handler(req, res) {
var resourceStream = stream()
// Send error over stream
resourceStream.error({ message: 'No data found for item 934' })
res(null, ok(resourceStream))
}
Sends a message over the stream:
return function handler(req, res) {
var resourceStream = stream()
// Send message over stream
resourceStream.message({ data: [1, 2, 3], id: 934 })
res(null, ok(resourceStream))
}
Sends an status data over the stream:
return function handler(req, res) {
var resourceStream = stream()
// Send status data over stream
resourceStream.status({ message: 'Reconnecting to server' })
res(null, ok(resourceStream))
}
A convenience methods for setting the accept type header to 'application/x.nap.view'
:
address('/price/usd/gbp')
.view()
.on('ok', function(response) {
// handle view function
})
.get()
A convenience methods for setting the accept type header to 'application/x.am.app'
:
address('/price/usd/gbp')
.app()
.on('ok', function(response) {
// handle view function
})
.get()
A convenience method to remove the need for boilerplate whilst also checking for error status codes
and validating the content type of the response. Using the into
api also triggers an update
event
on the target node as a hook for existing views.
This method can either takes a DOM node, though a valid CSS selector can also be used in place of a document element reference. If a CSS selector is provided, then the selection will be performed in the context of the document.
If you don't specify a target for the into
it will be targeted into the root node (.z-app
by default).
example: DOM node
var node = document.getElementsByClassName('.price')[0]
address('/price/usd/gbp')
.into(node)
.get()
example: CSS selector
address('/price/usd/gbp')
.into('.price')
.get()
example: without .into()
call
var node = document.getElementsByClassName('.price')[0]
address('/price/usd/gbp')
.on('ok', function(response) {
var view = response.body
view(node)
})
.get()
return function(req, res) {
res(null, ok(view))
function view (node) {
// render and listen to events on the DOM node
}
}
Dispatched before invoking a view function, regardless of the resource addressed into the DOM element of the view.
function view(node) {
node.addEventListener('update', handleUpdate)
function handleUpdate(detail) {
console.log('Updating resource from %s to %s.', deatail.from, detail.to)
}
}
Dispatched before invoking a view function with a new resource.
function view(node) {
node.addEventListener('resourcewillchange', handleResourceWillChange)
function handleResourceWillChange() {
console.log('Resource will change: do you want to clear the DOM?.')
}
}
Sometimes it is neccessary to perform a top level navigation by updating the browser address bar.
If a target is specified the request will be opened in the target window.
example: perform top level navigation
address('/price/usd/gbp')
.navigate()
example: perform navigation into target window
address('/price/usd/gbp')
.navigate('_blank')
Provides set or get of the resource navigation target to be requested:
address('/price/usd/gbp')
.target('_blank')
.navigate()