Handlers represent the mechanism that backs a resource. Each handler is an object expected to provide:
a constructor with an option parameter that can be used to inject any required handler specific configuration.
a ready property indicating the handler is ready to process requests.
some of the following methods:
initialise - when @jagql/framework loads, this is invoked once for every resource using this handler. Its an opportunity to allocate memory, connect to databases, etc.
close - for cleaning up upon jsonApi.close() (optional)
search - for searching for resources that match some vague parameters.
find - for finding a specific resource by id.
create - for creating a new instance of a resource.
delete - for deleting an existing resource.
update - for updating an existing resource.
Failure to provide the above handler functions will result in EFORBIDDEN HTTP errors if the corresponding REST routes are requested.
The rawResource Format
All data stored behind handlers should be stored in a developer-friendly format with both attributes and relations mingled together:
In the above example the photographer attribute is defined as relation to a resource of type people. @jagql/framework will deal with shuffling around and separating those attributes and relations when it needs to. Keep It Simple.
The request Format
All requests are presented to handlers in the following format:
{
params: {
// All request parameters get combined into this object. Query params, body params, etc.
foo: "bar"
},
headers: {
// All HTTP request headers
host: "localhost:16006",
connection: "keep-alive"
},
express: {
req, // Express req and res objects
res
}
route: {
// Routing information
host: "localhost:16006",
path: "/v1/swagger.json",
query: "foo=bar&baz=1",
combined: "https://localhost:16006/v1/swagger.json"
}
}
The error Format
All errors should be provided in the following format:
{
// The desired HTTP code
status: "404",
// A very short identifier for this error
code: "ENOTFOUND",
// A short human readable description
title: "Requested resource does not exist",
// Some detail to assist debugging
detail: "There is no "+request.params.type+" with id "+request.params.id
}
constructor
The handler object constructor can, depending on the handler's requirements, expect a object parameter which will contain any properties required for configuring the handler. For example if the handler uses a database for persistence the configuration object will contain the properties required to connect to the database.
ready
The ready property should be set to a truthy value once the handler is ready to process requests (which will usually happen at the end of initialise). If the handler is temporarily unable to process requests this property should be set to a falsy value during the down period.
handles<Sort|Filter>
Some post-process steps can be handled within the handler itself. For instance, some handlers may be capable of
returning data that is already sorted in the correct order. If the handlesSort or handlesFilter property is set to
a truthy value on the custom handler instance, then the corresponding post-processing step will be skipped.
The following flags can be set:
{
handlesSort: true; // skips the 'sort' post process step
handlesFilter: true; // skips the 'filter' post process step// . . .
}
initialise
initialise is invoked with the resourceConfig of each resource using this handler.
function(resourceConfig) { };
resourceConfig is the complete configuration object passed in to jsonApi.define().
close
close is invoked without any parameters, when jsonApi.close() is called.
It should close database connections, file handles, timers, event listeners, etc, as though initialise were never called.
search
search is invoked with a request object (see above).
function(request, callback) { };
the callback should be invoked with with an error or null, [ rawResource ], count.
search needs to watch for any request.params.relationships parameters, they represent foreign key lookups. An example of this:
translates to "Find me all of the resources whose user attribute is a link to a resource with id == ad3aa89e-9c5b-4ac9-a652-6670f9f27587".
find
find is invoked with a request object (see above).
function(request, callback){ };
the callback should be invoked with with an error or null, rawResource.
create
create is invoked with a request object (see above) AND a newResource object which is an instance of rawResource representing a validated instance of type request.params.type. The newResource will already have an id and is ready to be stored as per the resource definition.
function(request, newResource, callback) { };
the callback should be invoked with with an error or null, newResource.
delete
delete is invoked with a request object (see above). It should delete the resource of type request.params.type and id request.params.id.
function(request, callback){ };
the callback should be invoked with with an error or null.
update
update is invoked with a request object (see above) and a partialResource which represents a partial instance of rawResource - the properties of rawResource need to be merged over the original resource and saved.
function(request, partialResource, callback){ };
the callback should be invoked with with an error or null, newUpdatedResource.
Creating Custom Handlers
Handlers represent the mechanism that backs a resource. Each handler is an object expected to provide:
readyproperty indicating the handler is ready to process requests.initialise- when @jagql/framework loads, this is invoked once for every resource using this handler. Its an opportunity to allocate memory, connect to databases, etc.close- for cleaning up uponjsonApi.close()(optional)search- for searching for resources that match some vague parameters.find- for finding a specific resource by id.create- for creating a new instance of a resource.delete- for deleting an existing resource.update- for updating an existing resource.Failure to provide the above handler functions will result in
EFORBIDDENHTTP errors if the corresponding REST routes are requested.The
rawResourceFormatAll data stored behind handlers should be stored in a developer-friendly format with both attributes and relations mingled together:
{ id: "aab14844-97e7-401c-98c8-0bd5ec922d93", type: "photos", title: "Matrix Code", url: "http://www.example.com/foobar", photographer: { type: "people", id: "ad3aa89e-9c5b-4ac9-a652-6670f9f27587" } }In the above example the
photographerattribute is defined as relation to a resource of typepeople. @jagql/framework will deal with shuffling around and separating those attributes and relations when it needs to. Keep It Simple.The
requestFormatAll requests are presented to handlers in the following format:
{ params: { // All request parameters get combined into this object. Query params, body params, etc. foo: "bar" }, headers: { // All HTTP request headers host: "localhost:16006", connection: "keep-alive" }, express: { req, // Express req and res objects res } route: { // Routing information host: "localhost:16006", path: "/v1/swagger.json", query: "foo=bar&baz=1", combined: "https://localhost:16006/v1/swagger.json" } }The
errorFormatAll errors should be provided in the following format:
{ // The desired HTTP code status: "404", // A very short identifier for this error code: "ENOTFOUND", // A short human readable description title: "Requested resource does not exist", // Some detail to assist debugging detail: "There is no "+request.params.type+" with id "+request.params.id }constructor
The handler object constructor can, depending on the handler's requirements, expect a object parameter which will contain any properties required for configuring the handler. For example if the handler uses a database for persistence the configuration object will contain the properties required to connect to the database.
ready
The
readyproperty should be set to a truthy value once the handler is ready to process requests (which will usually happen at the end ofinitialise). If the handler is temporarily unable to process requests this property should be set to a falsy value during the down period.handles<Sort|Filter>
Some post-process steps can be handled within the handler itself. For instance, some handlers may be capable of returning data that is already sorted in the correct order. If the
handlesSortorhandlesFilterproperty is set to a truthy value on the custom handler instance, then the corresponding post-processing step will be skipped.The following flags can be set:
{ handlesSort: true; // skips the 'sort' post process step handlesFilter: true; // skips the 'filter' post process step // . . . }initialise
initialiseis invoked with theresourceConfigof each resource using this handler.function(resourceConfig) { };resourceConfigis the complete configuration object passed in tojsonApi.define().close
closeis invoked without any parameters, whenjsonApi.close()is called. It should close database connections, file handles, timers, event listeners, etc, as thoughinitialisewere never called.search
searchis invoked with arequestobject (see above).function(request, callback) { };the
callbackshould be invoked with with anerrorornull, [ rawResource ], count.searchneeds to watch for anyrequest.params.relationshipsparameters, they represent foreign key lookups. An example of this:request.params.relationships = { user: "ad3aa89e-9c5b-4ac9-a652-6670f9f27587" }translates to "Find me all of the resources whose user attribute is a link to a resource with id == ad3aa89e-9c5b-4ac9-a652-6670f9f27587".
find
findis invoked with arequestobject (see above).function(request, callback) { };the
callbackshould be invoked with with anerrorornull, rawResource.create
createis invoked with arequestobject (see above) AND anewResourceobject which is an instance ofrawResourcerepresenting a validated instance of typerequest.params.type. ThenewResourcewill already have anidand is ready to be stored as per the resource definition.function(request, newResource, callback) { };the
callbackshould be invoked with with anerrorornull, newResource.delete
deleteis invoked with arequestobject (see above). It should delete the resource of typerequest.params.typeand idrequest.params.id.function(request, callback) { };the
callbackshould be invoked with with anerrorornull.update
updateis invoked with arequestobject (see above) and apartialResourcewhich represents a partial instance ofrawResource- the properties ofrawResourceneed to be merged over the original resource and saved.function(request, partialResource, callback) { };the
callbackshould be invoked with with anerrorornull, newUpdatedResource.