liberated apis in clojureland - paris clojure user group
TRANSCRIPT
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Liberated APIs in ClojureLand
Paris Clojure User Group
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Gaylord Mazelier, CTO
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Rationale
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
• Why web services?
• How to design a good API?
• Why Clojure?
• Why Liberator?
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
The Clojure Web Stack
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Ring
• A low level spec for conforming libraries
• An ecosystem to compose your own stack
• Clojure maps for requests and responses
• Functions for handlers and middleware
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Requests and Responses
• Requests
• :server-port, :server-name,
• :remote-addr,
• :request-method, :uri, :query-string,
• :scheme, :headers and :body
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Requests and Responses
• Responses
• :status (HTTP status code)
• :headers
• :body (String, ISeq, File or InputStream)
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Requests and Responses
(-> (response "Hello World") (content-type "text/plain")) => {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"} (redirect "http://example.com") => {:status 302 :headers {"Location" "http://example.com"} :body ""}
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Handlers and Middleware• Handlers
• Do the work of your application
• Just function that takes and returns a map
• Ring request and response
• Middleware
• High-order functions
• Add functionality to handlers
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Handlers and Middleware
(defn what-is-my-ip [request] {:status 200 :headers {"Content-Type" "text/plain"} :body (:remote-addr request)}) (defn wrap-content-type [handler content-type] (fn [request] (let [response (handler request)] (assoc-in response [:headers "Content-Type"] content-type))))
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Adapters and Servers• Adapters
• Connecting a handler to a server
• Abstracting away details of that server
• Standardising to the Ring format
• Servers
• Host an HTTP server as part of your application
• Host your application as a servlet in a container
• Talk over some protocol to a web server outside your JVM
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Liberator Basics
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Webmachine• Resources
• A set of functions
• Referential transparency
• Small and isolated functions
• HTTP semantics
• A system with conventions
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Resources• Resources
• Ring handlers that adhere to the Ring contract
• resource function or defresource macro
• Developers
• Provide a set of functions
• Parametrised resources
• Base implementation
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Decisions• Decision points for resources implementors
• Resource behaviour
• allowed?, exists?, malformed?, etc.
• Internal decision points
• Override is possible but not useful in general
• accept-exists?, method-put?, etc.
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Decisions
• Context
• Single argument for all functions
• A map that initially contains
• :representation, :request, :response
• Updated by decision and action functions
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Decisions
• Decision functions result
• A boolean value
• Or a vector
• First element as the boolean decision
• Second element as the update to the context
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Actions• For side effects
• Evaluated like decision functions but
• The boolean value has no effect
• The next decision step is constant
• Action functions
• :post!, :put!, :delete!, :patch!
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Handlers• Final step of a request processing
• Must return a representation
• A function for each HTTP status code
• handle-ok for 200 OK
• Redirecting
• Corresponding handlers will set a Location header
• :location key in context or in resource definition
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Representations• Returned by handlers
• Protocol liberator.representation/Representation
• A single method as-response
• Returns a Ring response map
• The most types for response bodies are supported
• Can be represented as text, JSON, Clojure, etc.
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Decision Graph
• Handling a resource
• Executing a set of decisions
• Determining the correct response status code
• Generating the appropriate response
• Decisions are made according to a flowchart
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
In Practice
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Content Negotiation
(defn check-content-type "For PUT and POST, check if the content type is valid." [ctx content-types] (if (#{:put :post} (get-in ctx [:request :request-method])) (or (some #{(get-in ctx [:request :headers "content-type"])} content-types) [false {:message "Unsupported Content-Type"}]) true)) (defresource facts :available-media-types #{"application/edn"} :known-content-type? #(check-content-type % ["application/edn"]))
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Malformed Request
(defresource fact [id] :allowed-methods #{:get} :malformed? (fn [_] (if (ObjectId/isValid id) false {:message "Invalid id."})))
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
“Say Friend and enter.”(defn friend-auth "Returns a base resource that authenticates using the supplied auth-fn. Authorization failure will trigger Friend's default unauthorized response." [auth-fn] {:authorized? auth-fn}) (defn role-auth "Returns a base resource that authenticates users against the supplied set of roles." [role-input] (friend-auth (comp (roles? role-input) :request))) (defresource facts (role-auth #{:admin}) :handle-ok "This is OK")
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Others
• Logging
• ring.middleware.logger/wrap-with-logger
• Debugging
• liberator.dev/wrap-trace
• See this example
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Demo
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Final Thoughts
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
• Benefits
• HTTP semantics
• Productive and adaptable
• Readable resources behaviour
• wrap-trace middleware!
• Most-wanted features (or contributions :)
• Automatic content type negation for error responses
• Swagger integration
• OPTIONS support for self-describing resources
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
Sources
All Images: Copyright © 2015 ustwo studio Ltd. All rights reserved.
• https://brehaut.net/blog/2011/ring_introduction
• http://www.lispcast.com/parts-of-ring
• https://github.com/basho/webmachine
• http://en.wikipedia.org/wiki/Finite-state_machine
• http://clojure-liberator.github.io/liberator
• https://github.com/Prismatic/schema
• http://12factor.net/logs
• https://github.com/cemerick/friend
• http://zacstewart.com/2012/04/14/http-options-method.html
• https://www.mnot.net/blog/2014/06/07/rfc2616_is_dead
• http://evertpot.com/http-11-updated
• https://scott.mn/2014/01/26/first_thoughts_on_liberator_clojure
• https://github.com/pedestal/pedestal
• https://github.com/Prismatic/fnhouse