Deployd modules can register new Resource Types, which can be created with a route and configured per instance. Deployd comes with two built-in Resource Types: "Collection" and "User Collection". You can create your own custom resource types by extending the Resource constructor and implementing a handle()
method. Deployd will automatically load any Resource Types that are exported by a module.
Here is a simple custom resource type:
var Resource = require('deployd/lib/resource')
, util = require('util');
function Hello(name, options) {
Resource.apply(this, arguments);
}
util.inherits(Hello, Resource);
module.exports = Hello;
Hello.prototype.clientGeneration = true;
Hello.prototype.handle = function (ctx, next) {
if(ctx.req && ctx.req.method !== 'GET') return next();
ctx.done(null, {hello: 'world'});
}
This will allow you to add a "Hello" resource in the Dashboard. This resource will respond to every GET request with {"hello": "world"}
.
The most basic Custom Resource Type that is useful is the Event Resource, which will simply execute an On GET
event when it receives a GET
request and an On POST
event when it receives a POST
request.
This is the source for that module:
var Resource = require('deployd/lib/resource')
, util = require('util');
function EventResource() {
Resource.apply(this, arguments);
}
util.inherits(EventResource, Resource);
EventResource.label = "Event";
EventResource.events = ["get", "post"];
module.exports = EventResource;
EventResource.prototype.clientGeneration = true;
EventResource.prototype.handle = function (ctx, next) {
var parts = ctx.url.split('/').filter(function(p) { return p; });
var result = {};
var domain = {
url: ctx.url
, parts: parts
, query: ctx.query
, body: ctx.body
, 'this': result
, setResult: function(val) {
result = val;
}
};
if (ctx.method === "POST" && this.events.post) {
this.events.post.run(ctx, domain, function(err) {
ctx.done(err, result);
});
} else if (ctx.method === "GET" && this.events.get) {
this.events.get.run(ctx, domain, function(err) {
ctx.done(err, domain.result);
});
} else {
next();
}
};
Let's look at it line-by-line:
var Resource = require('deployd/lib/resource')
, util = require('util');
To create a Resource, you'll need the Resource module and Node's util module.
function EventResource() {
Resource.apply(this, arguments);
}
Creates the constructor for the EventResource
, also applying the base Resource
constructor.
util.inherits(EventResource, Resource);
Causes EventResource
to inherit its prototype from Resource
using Node's util.inherits() function.
EventResource.label = "Event";
Changes the Resource.label property to set how the EventResource
appears in the "Add Resource" menu in the Dashboard. Without this setting, it would appear using the constructor name, EventResource
.
EventResource.events = ["get", "post"];
Configures two events for the Resource type: get
and post
. These will appear on the "Events" page in the Dashboard with no extra configuration.
Note: The Dashboard provides the "On" prefix, e.g. "On Get"
module.exports = EventResource;
Exports the EventResource
constructor. This is how Deployd finds and loads the resource type.
EventResource.prototype.clientGeneration = true;
Sets the clientGeneration flag, which ensures that resources created with this resource type will be generated into dpd.js.
EventResource.prototype.handle = function (ctx, next) {
Defines a handle() function. This function will be called whenever a request is routed to this resource. The ctx
object is a Context, which includes useful properties (body, query, etc.) and functions (particularly done()) to simplify working with HTTP.
The next
function gives control back to the router.
var parts = ctx.url.split('/').filter(function(p) { return p; });
var result = {};
Set up some local variables; parts
is an array of the /
-separated parts in the URL.
var domain = {
url: ctx.url
, parts: parts
, query: ctx.query
, body: ctx.body
, 'this': result
, setResult: function(val) {
result = val;
}
};
Create a domain for the events. These are objects and functions that will be accessible from the event. Notice that the setResult
function is a closure that assigns its argument to the local result
variable.
if (ctx.method === "POST" && this.events.post) {
this.events.post.run(ctx, domain, function(err) {
ctx.done(err, result);
});
}
Run the POST
event using the Script.run() function if applicable, passing it the current context and domain.
The this.events object contains all of the available events; if the user has not written a POST
event, though, this.events.post
might be null
.
The callback for Script.run
returns an error err
if something went wrong in the script (or if the script writer called cancel())
Finally, call ctx.done() with both the error and result (result
is the local variable we set up earlier which the scriptwriter can change using setResult()
. The response body is always the second parameter, but it is ignored if an error is passed.
} else if (ctx.method === "GET" && this.events.get) {
this.events.get.run(ctx, domain, function(err) {
ctx.done(err, domain.result);
});
}
Do the same thing for the GET
event.
} else {
next();
}
If no event applies, call next()
. This tells the router that this resource cannot handle the current request, and the router will allow other resources to handle it. If every resource calls next()
, then Deployd will return a 404
status code.
Let us know if you have any ideas to improve our docs. Open an issue on github, send us an email, or tweet us.
This entire site, including documentation written in markdown is available on github. Pull requests are appreciated!