Hypermedia Application Protocol
@alexander_kielClojure Berlin 2015
REST
• Constraints: Client-server, Stateless, Cacheable, Layered System, Code on demand, Uniform interface
• Uniform interface: Identification of resources, Manipulation of resources through representations, Self-descriptive messages, Hypermedia as the engine of application state (HATEOAS)
– Roy T. Fielding
“REST APIs must be hypertext-driven.”
Hypermedia Application Protocol• Hypermedia type for REST API’s
• Like HTML for API’s
• Based on Transit as transport format
• Similar to HAL1 but adds Transit, Forms and Schema
• On GitHub: Spec, Browser, ToDo app, Ring middleware, Clojure(Script) client, Transit data types for Schema
1: http://stateless.co/hal_specification.html
RepresentationsRepresentations are maps with the following keys.
{"~:data": {} #_"Custom Data"
“~:links”: {} #_"Links"
“~:queries”: {} #_"Queries"
“~:forms”: {} #_"Forms"
“~:embedded”: {} #_”Embedded representations"
“~:ops”: {} #_"Operations"}
LinksMost important hypermedia artifact
{"~:links":
{"~:self": {"~:href": "~r/"}
"~:todo/items": {"~:href": "~r/items"}}}
LinksMost important hypermedia artifact
{"~:links":
{"~:self": {"~:href": "~r/"}
"~:todo/items": {"~:href": "~r/items"}}}
Every representation has links under the :links key.
LinksMost important hypermedia artifact
{"~:links":
{"~:self": {"~:href": "~r/"}
"~:todo/items": {"~:href": "~r/items"}}}
Every link has a key. Namespace less keys are reserved.
LinksMost important hypermedia artifact
{"~:links":
{"~:self": {"~:href": "~r/"}
"~:todo/items": {"~:href": "~r/items"}}}
Links can have: :href and :label.
LinksMost important hypermedia artifact
{"~:links":
{"~:self": {"~:href": "~r/"}
"~:todo/items": {"~:href": "~r/items"}}}
:href are Transit URI’s
QueriesLike HTML GET Forms
{"~:queries":
{"~:filter":
{"~:href": "~rhttp://..."
"~:params": {"~:filter": {"~:type": "~SStr"}}}}}
QueriesLike HTML GET Forms
{"~:queries":
{"~:todo/filter":
{"~:href": "~rhttp://..."
"~:params": {"~:filter": {"~:type": "~SStr"}}}}}
Every query has a key.
QueriesLike HTML GET Forms
{"~:queries":
{"~:todo/filter":
{"~:href": "~rhttp://..."
"~:params": {"~:filter": {"~:type": "~SStr"}}}}}
Queries can have: :href, :params and :label.
QueriesLike HTML GET Forms
{"~:queries":
{"~:todo/filter":
{"~:href": "~rhttp://..."
"~:params": {"~:filter": {"~:type": "~SStr"}}}}}
Params can have: :type, :optional and :label.
QueriesLike HTML GET Forms
{"~:queries":
{"~:todo/filter":
{"~:href": "~rhttp://..."
"~:params": {"~:filter": {"~:type": "~SStr"}}}}}
Param types are Prismatic Schemas.
FormsLike HTML POST Forms - Used to create resources.
{"~:forms":
{"~:todo/create-item":
{"~:href": "~r/items"
"~:params":
{“~:label": {"~:type": "~SStr"}}}}}
Same as structure queries but different semantic.
Custom Payload (Data)Data can be anything, but maps are most common.
{"~:data":
{“~:label”: “Buy milk.”
“~:state”: “~:active”}}
Param types are Prismatic Schemas.
Custom Payload (Data)Data can be anything, but maps are most common.
{"~:data":
{“~:label”: “Buy milk.”
“~:state”: “~:active”}}
Data is keyed under :data to separate from other things.
Embedded RepsRepresentations can be embedded for efficiency reasons.{"~:embedded":
{"~:todo/items":
[{"~:data": {"~:label": “Buy Milk.”}
"~:links": {"~:self": {"~:href": "~rhttp://..."}}}]}}
Embedded RepsRepresentations can be embedded for efficiency reasons.{"~:embedded":
{"~:todo/items":
[{"~:data": {"~:label": “Buy Milk.”}
"~:links": {"~:self": {"~:href": "~rhttp://..."}}}]}}
Reps are keyed under the same key as a link would be.
Embedded RepsRepresentations can be embedded for efficiency reasons.{"~:embedded":
{"~:todo/items":
[{"~:data": {"~:label": “Buy Milk.”}
"~:links": {"~:self": {"~:href": "~rhttp://..."}}}]}}
Reps still have a :self link.
Operations
• Tell the client what is possible / allowed
• Updates: PUT of the whole representation
• Delete: simple DELETE of the resource
{"~:ops": {"~#set": [“~:update" "~:delete"]}}
hap-client-cli
• Clojure(Script) lib build on top of HAP semantics
• High-level operations such as: fetch, execute (query), create, update and delete
• Uses core.async
Create a new Resource(require '[clojure.core.async :refer [go]]) (require '[async-error.core :refer [go-try <?]]) (require '[hap-client.core :as hap])
(go-try (let [root (<? (hap/fetch "http://localhost:8080")) form (:my-form (:forms root))] (<? (hap/create form {:name "value"})))))
Create a new Resource(require '[clojure.core.async :refer [go]]) (require '[async-error.core :refer [go-try <?]]) (require '[hap-client.core :as hap])
(go-try (let [root (<? (hap/fetch "http://localhost:8080")) form (:my-form (:forms root))] (<? (hap/create form {:name "value"})))))
• Fetch the service document containing the form
• Create the resource using the form
Top Related