A Siren Hypermedia Object Generator for Node
JavaScript
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
test
.eslintrc
.gitignore
.travis.yml
HISTORY.md
LICENSE
README.md
index.js
package.json

README.md

node-siren-writer

A generator for siren hypermedia API responses.

npm version npm dependencies npm dev dependencies build status

Example

var writer = require('siren-writer');
var siren = writer('http://api.x.io');

var entity = siren({
  class: 'order',
  properties: {
    orderNumber: 42,
    itemCount: 3,
    status: 'pending'
  },
  entities: [
    {
      class: [ 'items', 'collection' ],
      rel: 'http://x.io/rels/order-items',
      href: 'orders/42/items'
    },
    {
      class: [ 'info', 'customer' ],
      rel: 'http://x.io/rels/customer',
      properties: {
        customerId: 'pj123',
        name: 'Peter Joseph'
      },
      links: {
        rel: 'self',
        href: 'customers/pj123'
      }
    }
  ],
  actions: {
    name: 'add-item',
    title: 'Add Item',
    method: 'POST',
    href: 'orders/42/items',
    type: 'application/x-www-form-urlencoded',
    fields: [
      { name: 'orderNumber', type: 'hidden', value: '42' },
      { name: 'productCode', type: 'text' },
      { name: 'quantity', type: 'number' }
    ]
  },
  links: [
    {
      rel: 'self',
      href: 'orders/42'
    },
    {
      rel: 'previous',
      href: 'orders/41'
    },
    {
      rel: 'next',
      href: 'orders/43'
    }
  ]
});

console.log(entity);

will produce the example from the Siren homepage:

{
  "class": [ "order" ],
  "properties": {
      "orderNumber": 42,
      "itemCount": 3,
      "status": "pending"
  },
  "entities": [
    {
      "class": [ "items", "collection" ],
      "rel": [ "http://x.io/rels/order-items" ],
      "href": "http://api.x.io/orders/42/items"
    },
    {
      "class": [ "info", "customer" ],
      "rel": [ "http://x.io/rels/customer" ],
      "properties": {
        "customerId": "pj123",
        "name": "Peter Joseph"
      },
      "links": [
        { "rel": [ "self" ], "href": "http://api.x.io/customers/pj123" }
      ]
    }
  ],
  "actions": [
    {
      "name": "add-item",
      "title": "Add Item",
      "method": "POST",
      "href": "http://api.x.io/orders/42/items",
      "type": "application/x-www-form-urlencoded",
      "fields": [
        { "name": "orderNumber", "type": "hidden", "value": "42" },
        { "name": "productCode", "type": "text" },
        { "name": "quantity", "type": "number" }
      ]
    }
  ],
  "links": [
    { "rel": [ "self" ], "href": "http://api.x.io/orders/42" },
    { "rel": [ "previous" ], "href": "http://api.x.io/orders/41" },
    { "rel": [ "next" ], "href": "http://api.x.io/orders/43" }
  ]
}

API

writer(base)

Creates a new siren writer with the given base as the base URL for things like rel and href throughout an entity.

siren(options)

The returned function is the entire API. It returns an object that can be serialized as a JSON response.

// express
res.json(siren(/* options */));

// koa
this.body = siren(/* options */);

Generally-speaking, this API avoids performing magic. It wants you to be explicit, only accepting objects instead of positional arguments. However, there are a few ways that this API improves upon generating the response entirely from scratch:

  • ensures that single values are converted into arrays where they're required (eg: class, rel, etc)
  • flattens nested arrays into a single array, particularly for things like entities where you could be merging several different types for a single response
  • automatically resolves URLs relative to the base URL (eg: href and any rel values that aren't defined by IANA)
  • throws errors when you are missing things that are explicitly required, such as the href or rel for a link

The keys in options include everything in the spec, so use that as a reference when building your configuration options. Additional properties will be included, but their behavior with clients cannot be guaranteed. Some other things to be aware of:

  • as mentioned before, don't be too concerned with arrays if you're only setting a single value
  • don't be afraid to nest arrays, they will be flattened
  • include the trailing / in your base, especially when using a pathname
  • avoid using a leading / in your relative URLs, as it will behave unexpectedly if your base has a pathname as part of it. (ie: http://example.com/api/)
  • this library will throw exceptions in the cases mentioned previously