Skip to content

Signposting

Signposting is a technique used in RESTful APIs where other relevant resources are exposed to clients as Link headers in GET and HEAD requests. These Link headers follow a standard format as specified in RFC8288. Drupal already makes use of this technique for content entities, and Islandora takes it even further by providing additional Link headers that enable the client to navigate the repository and discover additional information about various resources. Because the links are returned in response headers, they can be relied upon without having to parse the message body. This makes them consistent across all serialization formats that can be returned in a message body (XML, JSON, JSONLD, etc...).

As a general precaution, link headers for Drupal entities are not exposed to users that do not have the permissions to view the entity linked in the header. So making GET and HEAD requests anonymously will yield a different set of headers than what an authenticated user would see. For example, anonymous users don't have the view media permission, so they will not see the link headers for media associated with a node.

Alternate Representations

Other representations generated by different serializers available through Drupal's REST API are exposed as link headers with rel="alternate" and type equal to the mimetype that will be received when dereferencing the link. For example, if an entity in Drupal has a JSONLD representation, then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/node/1?_format=jsonld>; rel="alternate"; type="application/ld+json"

Referenced Entities

Entity reference fields are exposed as link headers with rel="related" and a title equal to the entity reference field's display label. For example, if http://example.org/node/1 has an entity reference field name "Associated Content" that references http://example.org/node/2, then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/node/2>; rel="related"; title="Associated Content"

Referenced Taxonomy Terms

Entity reference fields for taxonomy terms get special handling. The taxonomy terms used to tag content are exposed as link headers with rel="tag" and a title equal to the taxonomy term's display label. If the term has an external URI in a controlled vocabulary, then that URI is provided. Otherwise, the local Drupal URI is provided. For example, if a piece of content is tagged with taxonomy/term/1, which has a display label of "Example Term", then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/taxonomy/term/1>; rel="tag"; title="Example Term"

If instead the term were to have the field_external_uri field with a value of http://exampletwo.org/vocab#term then the link header would look like

Link: <http://exampletwo.org/vocab#term>; rel="tag"; title="Example Term".

Associated Media

Media entities belonging to nodes are exposed as link headers with rel="related" and a title equal to the display label of their field_media_use taxonomy term. For example, if a media is tagged as Original File indicating that it is the initial file uploaded, the link header returned in a GET or HEAD response for a node would look like

Link: <http://example.org/media/1>; rel="related"; title="Original File".

Source Files

Files that are the source for media entities are exposed as Link headers in the GET and HEAD responses with rel="describes". The endpoint to edit the contents of the source file is also exposed using rel="edit-media". For example, if http://example.org/media/1 has the source file http://example.org/file.txt, then a GET or HEAD response would contain both

  • Link: <http://example.org/file.txt>; rel="describes"
  • Link: <http://example.org/media/1/source>; rel="edit-media"

Examples

Requesting a Node

After creating a node, adding it to a Collection, uploading a file and kicking off derivatives, the link headers returned for said node would look like the following. Note that non-Link headers have been removed for brevity:

vagrant@claw:~$ curl -I http://localhost:8000/node/1?_format=json
HTTP/1.1 200 OK
...
# These are provided by Drupal core
Link: <http://localhost:8000/node/1>; rel="canonical"
Link: <http://localhost:8000/node/1/delete>; rel="https://drupal.org/link-relations/delete-form"
Link: <http://localhost:8000/admin/content/node/delete?node=1>; rel="https://drupal.org/link-relations/delete-multiple-form"
Link: <http://localhost:8000/node/1/edit>; rel="edit-form"
Link: <http://localhost:8000/node/1/revisions>; rel="version-history"
Link: <http://localhost:8000/node/1>; rel="https://drupal.org/link-relations/revision"
Link: <http://localhost:8000/node?node=1>; rel="https://drupal.org/link-relations/create"

# These are provided by Islandora
Link: <http://localhost:8000/node/2>; rel="related"; title="Member of"
Link: <http://purl.org/coar/resource_type/c_c513>; rel="tag"; title="Image"
Link: <http://localhost:8000/media/1>; rel="related"; title="Original File"
Link: <http://localhost:8000/media/2>; rel="related"; title="Service File"
Link: <http://localhost:8000/media/3>; rel="related"; title="Thumbnail Image"
Link: <http://localhost:8000/node/1?_format=jsonld>; rel="alternate"; type="application/ld+json"

Requesting a Media

If we were to inspect one of the Media associated with this node (which we would've gotten in the response above), the results would look like:

vagrant@claw:~$ curl -I http://localhost:8000/media/1?_format=json
HTTP/1.1 200 OK
...

# These are provided by Drupal core
Link: <http://localhost:8000/media/add>; rel="https://drupal.org/link-relations/add-page"
Link: <http://localhost:8000/media/add/image>; rel="https://drupal.org/link-relations/add-form"
Link: <http://localhost:8000/media/1>; rel="canonical"
Link: <http://localhost:8000/admin/content/media>; rel="collection"
Link: <http://localhost:8000/media/1/delete>; rel="https://drupal.org/link-relations/delete-form"
Link: <http://localhost:8000/media/delete?media=1>; rel="https://drupal.org/link-relations/delete-multiple-form"
Link: <http://localhost:8000/media/1/edit>; rel="edit-form"
Link: <http://localhost:8000/media/1>; rel="https://drupal.org/link-relations/revision"

# These are provided by Islandora
Link: <http://localhost:8000/node/1>; rel="related"; title="Media of"
Link: <http://pcdm.org/use#OriginalFile>; rel="tag"; title="Original File"
Link: <http://localhost:8000/media/1?_format=jsonld>; rel="alternate"; type="application/ld+json"
Link: <http://localhost:8000/media/1/source>; rel="edit-media"
Link: <http://localhost:8000/_flysystem/fedora/2019-03/IF-Org-Chart_0.jpg>; rel="describes"; type="image/jpeg"

Last update: October 11, 2023