Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Pintura is the JSGI-based RESTful JSON/JavaScript server middleware stack that provides the web interface for the Persevere 2.0 framework

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.

Pintura is a cross-platform server side JavaScript based REST architecture web framework using standards based HTTP client/server interaction with a focus on JSON formatted data. Pintura gives you out of the box RESTful HTTP/JSON interface to data, you can simply create data models and Pintura automatically provides an HTTP interface. Pintura consists of reusable CommonJS modules and JSGI middleware such that it can be used on any JSGI compliant JavaScript platform, but is tested on NodeJS and RingoJS. Pintura forms the core of the Persevere 2.0 framework which is designed for rich Internet applications that rely heavily on Ajax-driven data communication from the browser. To learn more about features, capabilities, and philosophy of Pintura see the introduction to Pintura. The getting started with Pintura article provides a great starting point for using Pintura to build Persevere applications. Pintura is primarily composed of JSGI middleware components, and these components are described here.

Setup Pintura

Pintura can be installed with NPM via:

npm install pintura

However, one of the easiest way to get started with Pintura is start with the Persevere example app, which can be installed with:

npm install persevere-example-wiki

Pintura can be installed in RingoJS likewise:

ringo-admin install kriszyp/pintura

See the Persevere installation instructions for more information.

You can then use "app" property from require("pintura/pintura") as a JSGI application. With jsgi-node you can start Pintura:


You can see a more in-depth example of serving static files in combination with Pintura in the Persevere example app startup file.

Using Pintura

Persevere applications are generally built upon models, which act as classes for data. Perstore is the persistence library that is used by Persevere for defining models. Models defined with Perstore are automatically made accessible through HTTP by Pintura. The goal of Pintura is that your application will primarily consist of model design and Pintura will handle the most of the mechanics of exposing that model through an HTTP/REST API. A simple example of a model is:

var Model = require("perstore/model").Model,
    store = require("perstore/mongodb").MongoDB("Product");
Product = Model("Product",store, {
    properties: {
        name: String
        // we can define other properties, all 
    // we can define handlers
    put: function(object, directives){
        object.updated = new Date();
        store.put(object, directives);


With Persevere we can automatically interact with this data model through standard HTTP requests:

  • GET /{model}/{id} - Gets the object with the given id from the model store.
  • PUT /{model}/{id} - Updates or creates object with the given id in the model store.
  • DELETE /{model}/{id} - Deletes the object with the given id from the model store.
  • POST /{model}/ - Creates or incrementally updates an object in model store.

Pintura converts HTTP requests to method calls on the model. When an HTTP request is received it is converted to a call to the model of the form:

{model}.{httpMethod}({id or body},metadata);

For example if a request is received:

GET /Product/33 

This will result in a call to the model like:

Product.get("33", metadata); 

For the model above, there is no "get" method defined, so the default "get" handler would be used, which delegates to the store to get the object by id. If we made a request like:

PUT /Product/33 

This would call the "put" handler defined in the model above. One can also query stores through HTTP. Requests of the form /{model}/?{query} are passed through to the model by calls to the "query" method on the model. Perstore provides query parsing capabilities and stores implement query execution (dependent on the capabilities of the DB). An example of a query:

GET /Product/?type=shoe&price=lt=20&sort(-rating)


Persevere's facet-based security model forms the foundation of access control and is described in this article. Facets are used to define the different levels of access for models. Pintura's security configuration object can then be configured to define how users are authenticated and which facets or access levels each user is given. The security configuration object is available at require("pintura/pintura") The primary functions that can be overriden are:

  • authenticate(username, password) - The authenticate method allows you to define a custom authentication method and defaults to authenticating against the auto-generated User model. Should return a user object.
  • getAllowedFacets(user, request) - Allows you to define which facets are available for a given user. This should return an array of facets. By default this grants full access to everything (the require("pintura/security").FullAccess facet) for all users.
  • createUser(username, password) - This should create a new user for with the given credentials.
  • getUsername(user) - Should return the name of the given user.
  • {g|s}etUserClass - Retrieve or set the user class used to find users
  • {g|s}etAuthClass - Retrieve or set the authentication class used to find authentication tokens

Error Handling

Pintura includes middleware for catching errors and converting them to appropriate HTTP error status codes. The following uncaught errors (until the error middleware catches them) are translated:

  • URIError - 400
  • TypeError - 403
  • require("perstore/errors").NotFoundError - 404
  • require("perstore/errors").PreconditionFailed - 412
  • require("perstore/errors").AccessError - if user is authenticated 403, if not 401
  • require("perstore/errors").MethodNotAllowedError - 405
  • RangeError - 416
  • Other errors - 500 or if the error object has a "status" property, that will be used

Content Negotiation

One of the core concepts of the REST architecture is content negotiation which permits multiple views or representations of resources or objects. Providing content negotiation is a key functionality that Pintura provides. Pintura utilizes a set of media type handlers to find the best representation for serializing (or deserializing) data. Pintura comes with several media type handlers (found in pintura/media/) including:

  • json – JSON media handler
  • javascript – Similar to the JSON media handler, but will serialize to additional JavaScript specific types such as dates, NaN, functions, and other types that do not exist in JSON.
  • multipart-form-data and url-encoded – Used for parsing form data.
  • csv - Comma separated values
  • atom - Atom based view
  • html - A very simple HTML view of data.

To request a JSON view of data, include an Accept header in your HTTP request:

Accept: application/json

Accept headers can include multiple options and quality values. By default application/javascript is considered the highest quality represention by Pintura (it is basically the same as JSON but also can include date literals and special numeric types like NaN and Infinite).

Creating new media types is a common way to extend Pintura with additional formats. To create a new media type handler, use the Media constructor from the "media" module. This constructor takes an object argument with four properties:

  • mediaType - The name of the media type.
  • quality - A numeric value indicating the quality of the media type (generally a number from 0 - 1).
  • serialize - A function that is called to serialize the data (JavaScript objects or arrays) to string output for the response.
  • deserialize - A function that is called to deserialize the request input data to JavaScript objects or arrays.

Paging/Range Requests

Pintura can handle requests for "pages" of data, query results with start and ending index limits, through Range headers. To request items 10 through 19 of a query, include a Range header:

GET /Product/?type=shoe
Range: items=10-19

The server will return a Content-Range header indicating the range returned and total count of the results.

Bulk Updates and Comet

Pintura utilizes the message/* category of media types for indicating a set of requests or messages. Normally each HTTP request triggers one action in the store in its own transaction, but a request with a content type of message/sub-type (usually message/json or message/javascript) will be treated as a set of requests that are all processed within one transaction. This allows you to do several updates with one HTTP request. For request with a set of messages, the body should be an array of objects, where each object can have the following properties (only "method" is required):

  • to - The id/path of the target object of the action. This is resolved relative to the path of the request URI.
  • method - The method to execute, can be "get", "put", "post", "subscribe", or any other method on the target store.
  • body - The body of the request; the main payload of data.
  • queryString - query string
  • id - A message id, any subsequent message with the same id should be ignored (allows for idempotent messages)
  • metadata - Any metadata needed for the request

For example, updating two objects could be done:

POST /Product/
Content-Type: message/json 
Accept: message/json

  {to:"2", method:"put", body:{name:"updated 2"}, id: 1},
  {to:"3", method:"put", body:{name:"updated 3"}, id: 2}

The message/* media type can also be used in Accept headers to indicate that a response with a set of messages should be returned. This should be used for bulk updates. A response will be an array of objects where each object may have the following properties:

  • from - The id/path of the object that was acted on
  • body - The body of the response
  • id - The id of the message that this message is in response to
  • type - The type of the action that was executed

An example response (for the requests above):

Content-Type: message/json

  {"from":"2", "body":{"name":"updated 2"}, "id": 1},
  {"from":"3", "body":{"name":"updated 3"}, "id": 2}


The message/* media type can also be useful for real-time notification of events, AKA comet. Stores and models that support notifications can return observable objects, typically through the "subscribe" method, to indicate that multiple events may be emitted that can later be delivered to the client. When message requests are observable instead of direct value, responses will not be sent to the client until there is a message ready to be sent. For example, to subscribe to all events that take place on /User/john:

POST /User/
Content-Type: message/json
Client-Id: 251ab4ac9312f
Accept: message/json

  {to:"john", method:"subscribe"}

The response to the client will be delayed until an event/message for /User/john occurs.

For maximum browser compatibility, typically long-polling is used for comet applications. However, there is always a time gap between responses and the next request from the browser. Consequently for continuous gap-free subscriptions, it can be highly useful to emulate a continuous connection or queue for messages. This can be done by including a Client-Id header. Clients can generate a random id, and repeated connect using the same client id. Between requests, any events (from subscriptions) will be pushed into a queue for the given client id until the next request.

The Client-Id header can be included in standard requests as well, allowing other operations to add event sources and subscriptions to the current client queue.

Some browsers support XHR streaming and do not require long-polling repeated reconnections. If you wish to use streaming, include a Streaming header:

Streaming: true

The response will continue indefinitely, sending new messages as they occur.

Cross domain support

Pintura includes support for cross-domain requests from the browser/JavaScript through JSONP,, or CORS. To make a request with JSONP, you can do add a callback parameter



Pintura provides session management through session middleware. This middleware adds a getSession(createIfNecessary, expires) method to the request object. There is also a statically accessible exported function for accessing sessions:

require("pintura/jsgi/session").getCurrentSession(createIfNecessary, expires)

The session object is a persistent object and therefore the save() method that must be called if any changes are made to the session object (that need to be persisted to future requests).

Cross-Site Request Forgery Protection

Pintura provides CSRF protection to safeguard against malicious attempts to change data from other websites. This protection means that requests must prove that they are from your (same-origin) page and are therefore authorized requests. XHR requests can be validated by including a Client-Id header (with any value) to prove that the request was generated through XHR. Non-XHR requests (such as form-based requests) can prove their same-origin validation by including the cookie value from the "pintura-session" in a "pintura-session" query parameter.

If a request is not provably same-origin, the request object will include a "crossSiteForgeable" property value of true to indicate that it should be regarded with suspicion.


Pintura supports JSON-RPC to call methods on objects. One can call a method on a persisted object by using the URL for the object, and JSON-RPC encoded request entity that describes the method invocation to make. For example:

POST /Product/33
Content-Type: application/json
Accept: application/json

  params:["cool product"],

Pintura will then lookup the object with the id of "/Product/33" and call object.addNote("cool product"). The return value or thrown error from the call will be returned in a JSON-RPC response.


Below are the modules that are available in Pintura:


This module provides the default stack of Pintura middleware and an interface for configuring it. It also registers the default set of media types.


This is a JSGI application composed of the full stack of middleware that will expose your data models through RESTful requests.


This takes the Pintura configuration object. Properties on this object can be overriden to provide customize the behavior.


This function adds a connection to another server for the purposes of clustering. The connection object should conform to the WebSocket API, and provides a communication channel for the data to be shared.


This module provides a convenience function for easily starting Pintura on NodeJS using the jsgi-node delegator with WebSocket support.


This will start the given app, and route WebSocket requests through the app as well. For example:



This module is responsible for implementing authentication and authorization. Typically you would modify the pintura module's object to customize the security. However, the module also allows you to create new security objects.


This is a constructor that creates a new security object.

media (module)

This module is responsible for handling content negotiation, determining the appropriate media deserialization or renderer for a given content type or requested content type, by choosing the media type with the highest calculated quality setting for the negotiation.

This module provides a constructor for creating new media handlers that will be registered for the content negotiation process. This constructor is described in the "Content Negotiation" section above.

media (folder)

This folder contains modules that implement various media types. These media types can deserialize raw content to objects and serialize objects to raw content. These media types are registered by pintura module. Below are the media type modules, their name and default quality. The quality is a number between 0 - 1 that determines it's preference.

  • media/javascript - application/javascript, q=0.9: This represents utilizing JavaScript constructs like native Date objects, NaN, Infinite, etc. to extend JSON
  • media/json - application/json, q=0.8: JSON representation of objects
  • media/plain - text/plain, q=0.1
  • media/uri-list - text/uri-list, q=0.05: Represents arrays as a plain text list, one item per line
  • media/url-encoded - application/x-www-form-urlencoded, q=0.1: This is default encoding of forms in web pages, and is useful for decoding form data sent from form submissions
  • media/csv - text/csv, q=0.2: Comma seperated values representation
  • media/atom+xml - application/atom+xml, q=0.5: Atom feed representation of data
  • media/html - text/html, q=0.1: A simple default representation of data as HTML. If you are planning on rendering objects as HTML, you will probably want to register your own HTML media type handler. This handler only serializes.
  • media/multipart-form-data - multipart/form-data, q=0.2: Deserializes form data submitted with multipart/form-data as the content type. This media type is important for handling forms with file uploads
  • media/message/json - message/json, q=0.75: This is a representation of messages in JSON format. This can be used for triggering a series of actions in a single request. This is described in more detail above in the Bulk Updates section.


The jsgi folder contains the set of JSGI middleware components that comprise Pintura.


The auth module handles HTTP authorization, performing the HTTP request side of user authentication and calling the security module to perform the authentication and determine the authorization of the user. This module will set the "remoteUser" property on the request and the "currentUser" property on the promise context if a user is authenticated.


This module delegates the HTTP REST requests to the appropriate data model. This component will call the method on the model corresponding the request method name (converted to lowercase), so a PUT request will result in a model.put() call. The model is determined by the path of the request before the first slash. The first argument provided to the call will be the path for the requests without a body, and the body for requests with a body. The second argument is an object with the headers and the path as the "id" property.

This component will alternately call the query() method if the request is a GET with a query string. It will also handle the Range header, converting it to an appropriate limit() parameter in the query string.


This component processes the HTTP content negotiation headers, calling the pintura/media module to perform content negotiation. This handles the request body deserialization and response body serialization. The upstream middleware/apps can expect the request.body value to be a deserialized object (for example, JSON would be parsed), and can return an object, array, or other value in the response.body and this middleware component will serialize it based on the client's preferred media type (defined in the Accept header).


Source & Download:

Mailing list:


Pintura is part of the Persevere project, and therefore is licensed under the AFL or BSD license. The Persevere project is administered under the Dojo foundation, and all contributions require a Dojo CLA.

Something went wrong with that request. Please try again.