Skip to content

Instantly share code, notes, and snippets.

@Deraen
Last active December 15, 2019 08:03
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Deraen/9d65f447593859dd07ae to your computer and use it in GitHub Desktop.
Save Deraen/9d65f447593859dd07ae to your computer and use it in GitHub Desktop.
Compojure-api with Component
(ns foobar.components
(:require [com.stuartsierra.component :as component]
[compojure.api.sweet :refer :all]))
(defmethod compojure.api.meta/restructure-param :components
[_ components acc]
(update-in acc [:letks] into [components `(::components ~'+compojure-api-request+)]))
(defn wrap-components [handler components]
(fn [req]
(handler (assoc req ::components components))))
(defn make-handler
"Wrap a ring handler (e.g. routes from defapi) into middleware which will
assoc components to each request.
Handler is used through a var so that changes to routes will take effect
without restarting the system (e.g. re-evaulating the defapi form)"
[component]
(require 'foobar.handler)
(let [deps (select-keys component (:deps component))]
(-> (resolve 'foobar.handler/app)
(wrap-components deps))))
(ns foobar.db
(:require [monger.core :as mg]
[com.stuartsierra.component :as component]))
(defrecord Database [host db-name db-port opts conn]
component/Lifecycle
(start [component]
(let [conn (mg/connect {:host host
:port db-port})]
(assoc component
:conn conn
:db (mg/get-db conn db-name))))
(stop [{:keys [conn] :as component}]
(when conn (mg/disconnect conn))
;; NOTE: This is a record so dissoc is a bad idea
(assoc component
:conn nil
:db nil)))
(defn create-database [host db-name & [{:keys [db-port]}]]
(map->Database {:host host
:db-name db-name
:db-port (or db-port 27017)}))
(ns foobar.handler
(:require [ring.util.http-response :refer :all]
[compojure.api.sweet :refer :all]
[foobar.user :as user]))
(defapi app
(swagger-ui "/api-docs")
(swagger-docs
:title "Api thingies"
:description "playing with things")
(swaggered "session"
:description "Session"
(context "/session" []
(GET* "/" []
:return user/User
;; Take :db component from the request to db symbol
;; :components [db]
;; Uses letk: from :db component, take :db and bind it to db symbol
:components [[:db db]]
(ok (user/get-user db 1)))
;; Without c-api restructuring
(GET "/" req
(ok (user/get-user (-> req :foobar.components/components :db :db)))))))
(ns foobar.http-server
(:require [org.httpkit.server :refer [run-server]]
[com.stuartsierra.component :as component]))
(defrecord Http-server [opts db deps]
component/Lifecycle
(start [component]
(assoc component :http-kit (run-server (make-handler component)) opts)))
(stop [{:keys [http-kit] :as component}]
(when http-kit (http-kit))
(assoc component :http-kit nil)))
(defn create-http-server
"Creates Http-server which will use `make-handler` to create a ring handler
which has components given in first parameter bound into request map."
[deps port]
(map->http-server {:opts {:port port} :deps deps}))
(ns foobar.system
(:require [com.stuartsierra.component :as component]
[foobar.db :refer [create-database]]
[foobar.http-server :refer [create-http-server]]))
(defn base-system [{:keys [db-host db-name http-port] :as opts}]
(-> (component/system-map
:db (create-database db-host db-name)
:http-server (create-http-server [:db] http-port))
(component/system-using
{:http-server [:db]})))
(ns foobar.user
(:require [com.stuartsierra.component :as component]
[schema.core :as s]
[monger.collection :as mc]
[monger.util :as mu]))
(s/defschema User {:_id String
:name String})
(s/defn get-user :- User
[db id]
(mc/find-map-by-id db :users id))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment