This episode is for subscribers only. Sign Up or Log in to watch it.

13. Ring, pt.1

Published 06 August 16

In this new series you’ll build an API with Ring from the ground up. The pace is slow and no steps are skipped, making it a great lesson for beginners.

Show notes

This is the start of a new series of episodes about backend development from the ground up, and the first point of call, the foundation of any Clojure web app, is Ring.

Ring is based on a simple but powerful idea, borrowed from Ruby’s Rack and Python’s WSGI. If we can agree on a standard way to represent HTTP requests and HTTP responses, then web servers, web apps, middleware, and other tooling that follows this standard will just effortlessly click together, like legos. This standard is embodied in the Ring specification.

HTTP is a simple text based protocol, the client, the so-called “user agent” opens a connection to the server and sends a request that looks like this. I’m using an Emacs package called HTTP Twiddle, so I can send this request and see the server’s response.

The first line is the “Request Line”, it has three parts: the HTTP “method”, the resource path, and the HTTP version. In this case we want to GET the resource at the server root (“/”).

Following the request line are one or more headers. I’ve included a few common ones.

browse source code

This episode goes up to the ring-part-1 branch.

Code

project.clj

(defproject rolodex "0.1.0"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [ring/ring-jetty-adapter "1.5.0"]])

src/rolodex/core.clj

(ns rolodex.core
  (:require [ring.adapter.jetty :refer [run-jetty]]))

(defn random-uuid []
  (java.util.UUID/randomUUID))

(def contacts (atom (let [id (random-uuid)]
                      {id {:id id
                           :full-name "Trish Turtle"
                           :skills ["LISP" "Lambda Calculus"]}})))

(defn handler "request -> response"
  [req]
  (if (and (= (:request-method req) :get) (= (:uri req) "/"))
    {:status 200
     :headers {"Content-Type" "application/edn; charset=UTF-8"}
     :body (prn-str (vals @contacts))}))

(defonce server
  (run-jetty #'handler {:port 3000
                        :join? false}))