This document will describe how to model a resource using Rest.li. Before reading this page, you may want to get a feel for how to write and build code with Rest.li by reading the Tutorial to Create a Server and Client.
Rest.li is intended to foster the design of Uniform Interfaces. The idea of Uniform Interfaces is core to REST because it establishes standard patterns and behaviors that allow clients to explore and make sense of interfaces that they have never seen before.
Rest.li’s approach to uniform interface is based on REST principles and comprises the following:
Some resource types have an identifier, a name by which it can be accessed. Rest.li resource identifiers are URIs that follow specific patterns.
Rest.li messages are built with a JSON-encoded body, which provides a self-describing structure for the data in the message. The Rest.li framework provides convenient access to messages using Pegasus RecordTemplates.
Since Rest.li is built on HTTP, messages may also contain metadata in HTTP headers.
Resources are the nouns in the Rest.li world, and Resource Methods are the verbs. Rest.li provides a standard set of Resource Methods that describe what clients may do with the resources. These verbs are different than the HTTP verbs (GET, POST, PUT, DELETE, and so on) but map onto them (CREATE is a POST, UPDATE is a PUT, FIND is a GET).
CREATE, GET, UPDATE and DELETE also have a corresponding batch method. For example, BATCH_GET is used to read multiple entities and requires the client to provide a list of resource identifiers. It returns a map from identifiers to entities.
Each Rest.li resource endpoint is one of the following types: Collection, Simple, Association. Additionally, each resource endpoint may be a sub-resource of any other resource.
Collection is the most frequently used resource type. A collection models a key/value map of entities. It may be helpful to think of a database table with a primary key. Collections have a key type, used for identifying entities in the collection, and a value type used to represent the entity itself.
Collections can support all of the Rest.li resource methods described above.
Collections are declared by creating a class that extends CollectionResourceTemplate (or ComplexKeyResourceTemplate for complex keys, see below for details).
For example:
@RestLiCollection(name=“items”, …)
public class ItemsResource extends CollectionResourceTemplate\<Long,
Item\>
Defines a resource with a URI of the form:
/items/{longKey}
For keys with complex hierarchical data structures, use ComplexKeyResourceTemplate.
For Example:
@RestLiCollection(name=“widgets”, …)
public class WidgetResource implements extends
ComplexKeyResourceTemplate\<WidgetKey, EmptyRecord, Widget\>
Defines a resource with a URI of the form:
/widgets/number={number}\&thing.make={thing.make}\&thing.model={thing.model}
A simple resource models a singleton entity in a particular scope. Simple resources support the Rest.li resource methods GET, UPDATE and DELETE.
Simple resources are declared by creating a class that extends SimpleResourceTemplate.
For example:
@RestLiSimple(name=“selectedItem”, …)
public class SelectedItemResource extends SimpleResourceTemplate<Item>
Defines a resource with a URI of the form:
/selectedItem
Associations are structured like specialized collections but are used for a different modeling purpose. Associations model relationships between entities. Associations are like mapping tables in a database. Associations have a compound key consisting of multiple partial keys. Each partial key references one of the associated entities. Like collections, associations have a value type. However, in the case of associations, the value type is used to model attributes on the relationship between entities.
Associations support all of the Rest.li resource methods except for Create. Create requires the server to assign the resource identifier, which is incompatible with the constraint that an association’s partial keys are “foreign” keys from the referenced entities. Instead of using Create, new association relationships are made by using Update, which allows the client to provide the resource identifier.
For example:
@RestLiAssociation(name="myRelations", assocKeys={`Key(name=“key1”, type=long.class), @Key(name=“key2”, type=long.class)}, …)
public class MyRelationResource extends AssociationResourceTemplate<Relation> { … }
Defines a child resource with a URI of the form:
/myRelations/key1={longKey1}\&key2={longKey2}
Child resources are resources that are referenced through a parent resource. In Rest.li, the resource identifier for a child resource is an extension of the resource identifier for the parent resource. This means that the child resource has access to all of the information used to access the parent resource, including entity keys. A common pattern for using child resources is when accessing an element of a collection requires the key of another collection (the parent resource).
For example:
@RestLiCollection(parent=MyParentResource.class, name=“mySubResources”,…)
public class MySubResource extends CollectionResourceTemplate\<String,MySub\> { … }
Defines a child resource with a URI of the form:
/myParentResources/{parentResourceKey}/mySubResources/{stringKey}
Rest.li resources may be customized to handle a variety of use cases. The most common means of customization is to only implement the methods appropriate for a particular use case. Rest.li allows you to implement as many or as few of the resource methods as you choose. The framework will understand which methods you have implemented, and will advertise only those methods to clients.
Here are some examples.
A read-only collection can be used to expose entities that you do not want your clients to be able to modify. This might be useful if you need to expose a view which is derived from other data, or if you need to expose entities whose lifecycle is managed internally by your domain.
Read-Only collections can easily be implemented using Rest.li Collections, by only including read methods such as GET, BATCH_GET and FIND. By omitting any write operations (CREATE, UPDATE, DELETE) the collection becomes read-only.
Another variation on collections is a “natural key” collection, where the identifier for each entity is one of the domain attributes of the entity itself. (The alternative is a synthetic key collection, where the identifier is assigned arbitrarily, such as from a sequence number).
Natural key collections are implemented in Rest.li using Collections, by omitting the implementation for CREATE, and instead using UPDATE to add entities into the Collection. This is because CREATE is used when the server assigns the key for the entity. Although the name may be counter-intuitive, using UPDATE for this case is correct, because it is the resource method with the best defined semantics (PUT this entity representation at this location).
By extending ComplexKeyResourceTemplate instead of CollectionResourceTemplate, a collection may use any complex type (any defined pegasus record type) as a key.
A “Factory” Resource is used when the representation of an entity is assigned by the server using some input information provided by the client. For example, an authentication service might create a Session entity given a username and password, but the Session entity is created internally by the service.
To create a Factory resource in rest.li, use a Collection which only implements CREATE (and optionally DELETE). The value type of the Collection should be a Pegasus schema representing the input to the factory method. The key type should be the key for the resulting entity.
Note that a Factory would normally be paired with a Collection (perhaps a Read-Only Collection) which provides access to the entities themselves.
Rest.li collections have a single value type that is used for all of the resource methods supported by the collection. There are cases where you want to use different entity representations with the same key, for example, the input to CREATE is a different type than that returned by GET (See Factories section above). In such cases, you have two options:
Sometimes the same entity can be accessed using different sets of keys.
For example, Products belong to Companies. They are normally accessed efficiently by using both the CompanyId and ProductId, so the Product collection is a child resource of the Company collection. But if there is a legacy access path which uses only ProductId, this method cannot be modeled on the Product resource because the client does not have a key for the parent Company.
In such cases, you have a few options:
A frequently encountered question is how relationships between entities should be modeled. Suppose you have two objects, A and B - should you model their relationship by:
Here are some rules of thumb to help answer this question:
Associations are preferred when:
Fields are preferred (B is a field of A) when:
Subresources are preferred (B is a subresource of A) when:
Uniform Interfaces give us a powerful way to expose resources to the broadest possible set of clients. However, if you have a requirement which can’t reasonably be modeled using the uniform interface constructs, Rest.li does provide a loophole to help you. Actions are a special type of resource method which allow arbitrary RecordTemplate types as input and output parameters, and which have no constraints on semantics. It should be possible to model any operation as an Action, allowing you to fit your special case within Rest.li. However, because Actions do not conform to the Uniform Interface, actions cannot easily be used by higher-level frameworks like query languages, etc., which may be built on top of Rest.li. You should therefore avoid Actions whenever there is another reasonable option.