Current semantic version:
[octohipster "0.2.1-SNAPSHOT"]
Octohipster is
- a REST library/toolkit/microframework for Clojure
- that allows you to build HTTP APIs
- in a declarative Webmachine-like style, using Liberator
- powered by Ring; you can add rate limiting, authentication, metrics, URL rewriting and more with just middleware
It allows you to make APIs that
- support hypermedia (HAL+JSON, Collection+JSON and Link/Link-Template HTTP headers; works with Frenetic)
- support multiple output formats (JSON, EDN, YAML and any custom format)
- have Swagger documentation
- use JSON Schema for validation and documentation
- have pagination
- a resource is a single endpoint that accepts requests and returns responses
- a group is a collection of resources with a single URL prefix (eg. a group /things contains resources /things/ and /things/{id}) and zero or more shared properties (usually the schema)
- a documenter is a function that returns a resource which documents regular resources (Swagger, HAL root, etc)
- a mixin is a function that is applied to multiple resources to give them shared behavior (eg. collection or entry behavior)
- a response handler is a function that is used to encode response data to a particular content-type (JSON, EDN, YAML, etc.)
- a params handler is a function that is used to decode incoming data from a particular content-type (JSON, EDN, YAML, etc.)
(ns example
(:use [octohipster core routes mixins pagination]
[octohipster.documenters swagger schema]
org.httpkit.server)
(:import org.bson.types.ObjectId)
(:require [monger.core :as mg]
[monger.query :as mq]
[monger.collection :as mc]
monger.json))
(mg/connect!)
(mg/set-db! (mg/get-db "octohipster-example"))
;;;; The "model"
;;;; tip: make it a separate namespace, eg. app.models.contact
(def contact-schema
{:id "Contact"
:type "object"
:properties {:name {:type "string"}
:phone {:type "integer"}}
:required [:name]})
(defn contacts-count [] (mc/count "contacts"))
(defn contacts-all []
(mq/with-collection "contacts"
(mq/find {})
(mq/skip *skip*)
(mq/limit *limit*)))
(defn contacts-find-by-id [x] (mc/find-map-by-id "contacts" (ObjectId. x)))
(defn contacts-insert! [x]
(let [id (ObjectId.)]
(mc/insert "contacts" (assoc x :_id id))
(mc/find-map-by-id "contacts" id)))
(defn contacts-update! [x old] (mc/update "contacts" old x :multi false))
(defn contacts-delete! [x] (mc/remove "contacts" x))
;;;; The resources
;; with shared pieces of documentation
(def name-param
{:name "name", :dataType "string", :paramType "path", :required "true", :description "The name of the contact", :allowMultiple false})
(def body-param
{:dataType "Contact", :paramType "body", :required true, :allowMultiple false})
(defresource contact-collection
:desc "Operations with multiple contacts"
:mixins [collection-resource]
:clinks {:item ::contact-item}
:data-key :contacts
:exists? (fn [ctx] {:contacts (contacts-all)})
:post! (fn [ctx] {:item (-> ctx :request :non-query-params contacts-insert!)})
:count (fn [req] (contacts-count))
:doc {:get {:nickname "getContacts", :summary "Get all contacts"}
:post {:nickname "createContact", :summary "Create a contact"}})
(defresource contact-item
:desc "Operations with individual contacts"
:url "/{_id}"
:mixins [item-resource]
:clinks {:collection ::contact-collection}
:data-key :contact
:exists? (fn [ctx]
(if-let [doc (-> ctx :request :route-params :_id contacts-find-by-id)]
{:contact doc}))
:put! (fn [ctx]
(-> ctx :request :non-query-params (contacts-update! (:contact ctx)))
{:contact (-> ctx :request :route-params :_id contacts-find-by-id)})
:delete! (fn [ctx]
(-> ctx :contact contacts-delete!)
{:contact nil})
:doc {:get {:nickname "getContact", :summary "Get a contact", :parameters [name-param]}
:put {:nickname "updateContact", :summary "Overwrite a contact", :parameters [name-param body-param]}
:delete {:nickname "deleteContact", :summary "Delete a contact", :parameters [name-param]}})
;;;; The group
(defgroup contact-group
:url "/contacts"
:add-to-resources {:schema contact-schema} ; instead of typing the same for all resources in the group
:resources [contact-collection contact-item])
;;;; The handler
(defroutes site
:groups [contact-group]
:documenters [schema-doc schema-root-doc swagger-doc swagger-root-doc])
(defn -main [] (run-server site {:port 8080}))
Also, API Documentation is available.
By participating in this project you agree to follow the Contributor Code of Conduct.
Please take over the whole project!
I don't use Clojure a lot nowadays.
Talk to me: greg@unrelenting.technology.
This is free and unencumbered software released into the public domain.
For more information, please refer to the UNLICENSE
file or unlicense.org.