Extensions

Lacinia makes it possible to add extension data to the result map from within field resolvers. This extension data is exposed as the :extensions key on the result map (alongside the more familiar :data and :errors keys).

The GraphQL specification allow for extensions but leaves them entirely up to the application to define and use - there’s no validation at all. One example of extension data is tracing information that Lacinia can optionally include in the result.

More general extension data is introduced using modifier functions defined in the com.walmartlabs.lacinia.resolve namespace.

Extension Data

The with-extensions function is used to introduce data into the result. with-extension is provided with a function, and additional optional arguments, and is used to modify the extension data to a new state.

A hypothetical example; perhaps you would like to identify the total amount of time spent performing database access, and return a total in the :total-db-access-ms extension key.

You might instrument each resolver that accesses the database to calculate the elapsed time.

(require '[com.walmartlabs.lacinia.resolve :refer [with-extensions]])

(defn resolve-user
  [context args value]
  (let [start-ms (System/currentTimeMillis)
        user-data (get-user-data (:id args))
        elapsed-ms (- (System/currentTimeMillis) start-ms)]
    (with-extensions user-data
      update :total-db-access-ms (fnil + 0) elapsed-ms)))

The call to with-extensions adds the elapsed time for this call to the key (the fnil function lets the + function treat nil as 0).

This data is exposed in the final result map:

{:data
 {:user
  {:id "thx1138"
   :name "Thex"}}
 :extensions
 {:total-db-access-ms 3}}

You should be aware that field resolvers may run in an unpredictable order, especially when asynchronous field resolvers are involved. Complex update logic may be problematic if one field resolver expects to modify extension data introduced by a different field resolver. Sticking with assoc-in or update-in is a good bet.

Warnings

The with-error modifier function adds an error map to a result; in general, errors are serious - Lacinia adds errors when it can’t parse the GraphQL document, for example.

Lacinia adds a less dramatic level, a warning, via the with-warning modifier. with-warning adds an error map to the :warnings map stored in the :extensions key.

What a client does with errors and warnings is up to the application. In general, errors should indicate a failure of the overall request, and an interactive client might display an alert dialog related to the request and response.

An interactive client may present warnings differently, perhaps adding an icon to the user view to indicate that there were non-fatal issues executing the query.