patchboard
v0.5.4
Published
Patchboard API server and tools
Downloads
48
Readme
Patchboard
Patchboard streamlines the development of REST API services by using a serializable API definition that is easy to write and easy to read. With it, you can eliminate the repetition and brittleness involved in writing and maintaining an API provided a server and used by clients in multiple languages.
Given a description of your URLs, resources, and schemas, Patchboard provides a Node.js server that validates HTTP requests and dispatches them to resource handlers you define. The server exposes the API Definition as JSON, which a Patchboard client can use to self-assemble the logic needed to interact with the API.
Because the API Definition is serializable, Patchboard clients that consume it at runtime can be implemented in any sufficiently dynamic language. At this time, only a JavaScript client has been written.
API Definition format
Examples are excerpts from the Trivial example, which is written in CoffeeScript.
Mappings
A dictionary that maps arbitrary logical names to resources and the URLs used to access them.
module.exports =
users:
resource: "users"
path: "/users"
user_search:
resource: "user"
path: "/user"
query:
login:
required: true
type: "string"
user:
resource: "user"
template: "/users/:id"
Schemas
A JSON Schema (currently limited to draft 3) describing the data structures used by an API. Resource schemas are defined as properties of the top level definitions
field.
media_type = (name) ->
"application/vnd.trivial.#{name}+json;version=1.0"
module.exports =
id: "urn:patchboard.trivial"
definitions:
resource:
extends: {$ref: "urn:patchboard#resource"}
user:
extends: {$ref: "#resource"}
mediaType: media_type "user"
properties:
login:
required: true
type: "string"
pattern: "^[a-zA-z0-9_.]{3,32}"
email:
type: "string"
format: "email"
password:
type: "string"
minLength: 4
maxLength: 64
questions: {$ref: "#questions"}
Resources
A dictionary describing the resources provided by the API. Each top level field defines a resource.
An excerpt of the Trivial example:
type = (name) ->
"application/vnd.trivial.#{name}+json;version=1.0"
module.exports =
users:
actions:
create:
method: "POST"
request:
type: type "user"
response:
type: type "user"
status: 201
user_search:
actions:
get:
method: "GET"
response:
type: type "user"
status: 200
user:
actions:
get:
method: "GET"
response:
type: type "user"
status: 200
delete:
method: "DELETE"
response:
status: 204
For each resource, the actions
dictionary defines the available actions, specifying the HTTP method, the request and/or response media types, and the status that signifies a successful response.
Implementing a Patchboard Service
Validate your definition
Patchboard includes a JSON Schema that describes a valid API Definition. A convenient way to validate your definition is to use bin/patchboard
.
bin/patchboard validate path/to/api.json # or api.coffee, or api.js
You can also print a basic example definition to STDOUT.
bin/patchboard example json # or cson
Patchboard.Server
Patchboard = require "patchboard"
api = require "./api"
handlers = require "./handlers"
server = new Patchboard.Server api,
# Host and port to listen for HTTP requests
host: "127.0.0.1", port: 1979
# The base URL for resources
url: "http://localhost:1979/"
handlers: handlers
Request Handlers
The request handler object is a dictionary of dictionaries. The top level fields correspond to resource names, with the fields in the values mapping to actions. For each resource + action, you define a function which takes a request Context object as its argument.
In the following simplified example, the module exports a function which takes an application instance as its argument, then returns the handlers dictionary.
module.exports = (application) ->
users:
create: (context) ->
{match, request} = context
content = request.body
application.create_user content, (error, result) ->
if error
context.error error.name, error.message
else
context.respond match.success_status, result
user:
get: (context) ->
application.get_user context.match.path.id, (error, result) ->
if error
context.error error.name, error.message
else
context.respond match.success_status, result
update: (context) ->
{match, request} = context
id = match.path.id
content = request.body
application.update_user id, content, (error, result) ->
if error
context.error error.name, error.message
else
context.respond match.success_status, result
Note that the callback passed to the application methods could easily be refactored into a function and used as the default for simple actions.
Request Context
The default Patchboard dispatcher calls handler functions with an instance of Context as an argument. Contexts bundle together the objects representing the HTTP request and response, as well as providing helper functions such as respond
and error
Using a Patchboard Client
Example use of the JavaScript client (in CoffeeScript).
Client = require("patchboard").Client
# or you can require "patchboard-js" directly
# retrieve the API Definition from a Patchboard server and assemble a client.
Client.discover "http://localhost:1979/", (error, client) ->
throw error if error
client.resources.users.create {login: "matthew"}, (error, response) ->
throw error if error
user = response.resource
user.update {email: "[email protected]"}, (error, response) ->