Skip to content

Modeling

Dillon Redding edited this page Dec 31, 2023 · 7 revisions

The data model is implemented using classes, each representing an element defined in the specification.

Siren.js Client Model

Example

Let’s take a look at how we use this model to build the example entity from the Siren specification:

import { Action, EmbeddedEntity, EmbeddedLink, Entity, Field, Link } from '@siren-js/client';

const embeddedOrderedItemsLink = new EmbeddedLink();
embeddedOrderedItemsLink.class = ['items', 'collection'];
embeddedOrderedItemsLink.rel = ['http://x.io/rels/order-items'];
embeddedOrderedItemsLink.href = 'http://api.x.io/orders/42/items';

const customerLink = new Link();
customerLink.rel = ['self'];
customerLink.href = 'http://api.x.io/customers/pj123';

interface EmeddedCustomerProperties {
  customerId: string;
  name: string;
}

const embeddedCustomerEntity = new EmbeddedEntity<EmeddedCustomerProperties>();
embeddedCustomerEntity.class = ['info', 'customer'];
embeddedCustomerEntity.rel = ['http://x.io/rels/customer'];
embeddedCustomerEntity.properties = {
  customerId: 'pj123',
  name: 'Peter Joseph'
};
embeddedCustomerEntity.links = [customerLink];

const orderNumberField = new Field<string>();
orderNumberField.name = 'orderNumber';
orderNumberField.type = 'hidden';
orderNumberField.value = '42';

const productCodeField = new Field<string>();
productCodeField.name = 'productCode';
// Field's type defaults to 'text' 🎉

const quantityField = new Field<number>();
quantityField.name = 'quantity';
quantityField.type = 'number';

const addItemAction = new Action();
addItemAction.name = 'add-item';
addItemAction.title = 'Add Item';
addItemAction.method = 'POST';
addItemAction.href = 'http://api.x.io/orders/42/items';
addItemAction.fields = [orderNumberField, productCodeField, quantityField];
// Action's type defaults to 'application/x-www-form-urlencoded' 🎉

const selfLink = new Link();
selfLink.rel = ['self'];
selfLink.href = 'http://api.x.io/orders/42';

const previousLink = new Link();
selfLink.rel = ['previous'];
selfLink.href = 'http://api.x.io/orders/41';

const nextLink = new Link();
selfLink.rel = ['next'];
selfLink.href = 'http://api.x.io/orders/43';

interface OrderProperties {
	orderNumber: number;
  itemCount: number;
  status: string;
}

const orderEntity = new Entity<OrderProperties>();
orderEntity.class = ['order'];
orderEntity.properties = {
  orderNumber: 42,
  itemCount: 3,
  status: 'pending'
};
orderEntity.entities = [embeddedOrderedItemsLink, embeddedCustomerEntity];
orderEntity.actions = [addItemAction];
orderEntity.links = [selfLink, previousLink, nextLink];

Pretty ugly, right? Luckily clients don’t need to build entities this way. Instead they receive an entity from a server, which is where parsing comes into play. However, we will reference these objects throughout the wiki using the variable names above.

Lookup Methods

The Entity and EmbeddedEntity classes define a method for looking up one of its actions by name.

orderEntity.getAction('add-item');
//=> returns the addItemAction object

embeddedCustomerEntity.getAction('deactivate');
//=> returns undefined

Additionally, the Action class defines a similar method for looking up one of its fields by name.

addItemAction.getField('quantity');
//=> returns the quantityField object

addItemAction.getField('description');
//=> returns undefined

Accepting a Visitor

Each model class implements the SirenElement interface, meaning any element can accept a SirenElementVisitor. Check out Traversing for more details.

Clone this wiki locally