Lacinia Pedestal

Working from the REPL is important, but ultimately GraphQL exists to provide a web-based API. Fortunately, it is very easy to get your Lacinia application up on the web, on top of the Pedestal web tier, using Lacinia-Pedestal.

In addition, for free, we get GraphQL’s own REPL: GraphiQL.

Add Dependencies

project.clj
(defproject clojure-game-geek "0.1.0-SNAPSHOT"
  :description "A tiny BoardGameGeek clone written in Clojure with Lacinia"
  :url "https://github.com/walmartlabs/clojure-game-geek"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [com.walmartlabs/lacinia-pedestal "0.5.0"]
                 [io.aviso/logging "0.2.0"]])

We’ve added two libraries: lacinia-pedestal and io.aviso/logging. We no longer need to list lacinia, as that is a transitive dependency of lacinia-pedestal. [1]

The former brings in quite a few dependencies, including Pedestal, and the underlying Jetty layer that Pedestal builds upon.

The io.aviso/logging library sets up Logback as the logging library.

Clojure and Java are both rich with web and logging frameworks; Pedestal and Logback are simply particular choices that we’ve made and prefer; many other people are using Lacinia on the web without using Logback or Pedestal.

Some Configuration

For best results, we can configure Logback; this keeps startup and request handling from being very chatty:

dev-resources/logback-test.xml
<configuration scan="true" scanPeriod="1 seconds">

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%-5level %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="warn">
    <appender-ref ref="STDOUT"/>
  </root>

</configuration>

This configuration hides log events below the warning level (that is, debug and info events). If any warnings or errors do occur, minimal output is sent to the console.

A logback-test.xml takes precendence over the production logback.xml configuration we will eventually supply.

User Namespace

We’ll add more scaffolding to the user namespace, to make it possible to start and stop the Pedestal server.

dev-resources/user.clj
(ns user
  (:require
    [clojure-game-geek.schema :as s]
    [com.walmartlabs.lacinia :as lacinia]
    [com.walmartlabs.lacinia.pedestal :as lp]
    [io.pedestal.http :as http]
    [clojure.java.browse :refer [browse-url]]
    [clojure.walk :as walk])
  (:import (clojure.lang IPersistentMap)))

(def schema (s/load-schema))

(defn simplify
  "Converts all ordered maps nested within the map into standard hash maps, and
   sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems."
  [m]
  (walk/postwalk
    (fn [node]
      (cond
        (instance? IPersistentMap node)
        (into {} node)

        (seq? node)
        (vec node)

        :else
        node))
    m))

(defn q
  [query-string]
  (-> (lacinia/execute schema query-string nil nil)
      simplify))

(defonce server nil)

(defn start-server
  [_]
  (let [server (-> schema
                   (lp/service-map {:graphiql true})
                   http/create-server
                   http/start)]
    (browse-url "http://localhost:8888/")
    server))

(defn stop-server
  [server]
  (http/stop server)
  nil)

(defn start
  []
  (alter-var-root #'server start-server)
  :started)

(defn stop
  []
  (alter-var-root #'server stop-server)
  :stopped)

This new code is almost entirely boilerplate for Pedestal and for Lacinia-Pedestal. The core function is com.walmartlabs.lacinia.pedestal/service-map which is passed the compiled schema and a map of options, and returns a Pedestal service map which is then used to create the Pedestal server.

GraphiQL is not enabled by default; it is opt-in, and should generally only be enabled for development servers, or behind a firewall that limits access from the outside world.

Lacinia-Pedestal services GraphQL requests at the /graphql path. The default port is 8888. It handles both GET and POST requests. We’ll get to the details later.

The / and /index.html paths, and related JavaScript and CSS resources, can only be accessed when GraphiQL is enabled.

Starting The Server

With the above scaffolding in place, it is just a matter of starting the REPL and evaluating (start).

At this point, your web browser should open to the GraphiQL application:

../_images/graphiql-initial.png

Tip

It’s really worth following along with this section, especially if you haven’t played with GraphiQL before. GraphiQL assists you with formatting, provides pop-up help, flags errors in your query, and supplies automatic input completion. It can even pretty print your query. It makes for quite the demo!

Running Queries

We can now type a query into the large text area on the left and then click the right arrow button (or type Command+Enter), and see the server response as pretty-printed JSON on the right:

../_images/graphiql-basic-query.png

Notice that the URL bar in the browser has updated: it contains the full query string. This means that you can bookmark a query you like for later (though it’s easier to do that using the History button). Alternately, and more importantly, you can copy that URL and provide it to other developers. They can start up the application on their workstations and see exactly what you see, a real boon for describing and diagnosing problems.

This approach works even better when you keep a GraphQL server running on a shared staging server. On split [2] teams, the developers creating the application can easily explore the interface exposed by the GraphQL server, even before writing their first line of code.

Trust me, they love that.

Documentation Browser

The < Docs button on the right opens the documentation browser:

../_images/graphiql-doc-browser.png

The documentation browser is invaluable: it allows you to navigate around your schema, drilling down to queries, objects, and fields to see a summary of each declaration, as well as documentation - those :description values we added way back at the beginning.

Take some time to learn what GraphiQL can do for you.

[1]Occasionally, you’ll list an explicit lacinia dependency to get a newer version than the one lacinia-pedestal declares. Adding such a dependency directly to your project.clj is the correct way to override such a transitive dependency.
[2]That is, where one team or set of developers just does the user interface, and the other team just does the server side (including Lacinia). Part of the value proposition for GraphQL is how clean and uniform this split can be.