Lacinia Manual |Lacinia Tutorial |API Documentation
This library is a full implementation ofFacebook's GraphQL specification.
Lacinia should be viewed as roughly analogous to theofficial reference JavaScript implementation.In other words, it is a backend-agnostic GraphQL query execution engine.Lacinia is not an Object Relational Mapper ... it's simply the implementation of a contractsitting between the GraphQL client and your data.
Lacinia features:
An EDN-based schema language.
High performance parser for GraphQL queries, built on Antlr4.
Efficient and asynchronous query execution.
Full support for GraphQL types, interfaces, unions, enums, input objects, and custom scalars.
Full support for GraphQL subscriptions.
Full support of inline and named query fragments.
Full support for GraphQL Schema Introspection.
Lacinia has been developed with a set of core philosophies:
Prefer data over macros and other tricks. Compose your schema in whatever mix of data and code works for you.
Embrace Clojure: Use EDN data, keywords, functions, and persistent data structures.
Keep it simple: You provide the schema and a handful of functions to resolve data, and Lacinia does the rest.
Do the right thing: apply reasonable defaults without a lot of "magic".
This library can be plugged into any Clojure HTTP pipeline.The companion library lacinia-pedestal providesfull HTTP support, including GraphQL subscriptions, for Pedestal.
An externally developed library, duct-lacinia, provides similar capabilityfor Duct.
For more detailed documentation, read the manual.
GraphQL starts with a schema definition of exposed types.
A schema starts as an EDN file; the example below demonstrates a small subsetof the available options:
{:enums
{:episode
{:description "The episodes of the original Star Wars trilogy."
:values [:NEWHOPE :EMPIRE :JEDI]}}
:objects
{:droid
{:fields {:primary_functions {:type (list String)}
:id {:type Int}
:name {:type String}
:appears_in {:type (list :episode)}}}
:human
{:fields {:id {:type Int}
:name {:type String}
:home_planet {:type String}
:appears_in {:type (list :episode)}}}}
:queries
{:hero {:type (non-null :human)
:args {:episode {:type :episode}}
:resolve :get-hero}
:droid {:type :droid
:args {:id {:type String :default-value "2001"}}
:resolve :get-droid}}}
A schema alone describes what data is available to clients, but doesn't identify wherethe data comes from; that's the job of a field resolver, provided by the:resolve key inside fields such as the :hero and :droid query.
The values here, :get-hero and :get-droid, are placeholders; the startup codeof the application will usecom.walmartlabs.lacinia.util/attach-resolvers
to attach the actualfield resolver function.
A field resolver is just a function which is passed the application context,a map of arguments values, and a resolved value from aparent field.The field resolver returns a value. If it's a scalar type, it should return a valuethat conforms to the defined type in the schema.If not, it's a type error.
The field resolver is totally responsible for obtaining the data from whateverexternal store you use: whether it is a database, a web service, or somethingelse.
It's important to understand that every field has a field resolver, even ifyou don't define it explicitly. If you don't supply a field resolver,Lacinia provides a default field resolver, customized to the field.
Here's what the get-hero
field resolver might look like:
(defn get-hero [context arguments value]
(let [{:keys [episode]} arguments]
(if (= episode :NEWHOPE)
{:id 1000
:name "Luke"
:home_planet "Tatooine"
:appears_in ["NEWHOPE" "EMPIRE" "JEDI"]}
{:id 2000
:name "Lando Calrissian"
:home_planet "Socorro"
:appears_in ["EMPIRE" "JEDI"]})))
In this greatly simplified example, the field resolver can simply return the resolved value.Field resolvers that return multiple values return a list, vector, or set of values.
In real applications, a field resolver might execute a query against a database,or send a request to another web service.
After attaching resolvers, it is necessary to compile the schema; thisstep performs validations, provide defaults, and organizes the schemafor efficient execution of queries.
This needs only be done once, in application startup code:
(require '[clojure.edn :as edn]
'[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
'[com.walmartlabs.lacinia.schema :as schema])
(def star-wars-schema
(-> "schema.edn"
slurp
edn/read-string
(attach-resolvers {:get-hero get-hero
:get-droid (constantly {})})
schema/compile))
With the compiled application available, it can be used to executerequests; this typically occurs inside a Ring handler function:
(require '[com.walmartlabs.lacinia :refer [execute]]
'[clojure.data.json :as json])
(defn handler [request]
{:status 200
:headers {"Content-Type" "application/json"}
:body (let [query (get-in request [:query-params :query])
result (execute star-wars-schema query nil nil)]
(json/write-str result))})
Lacinia doesn't know about the web tier at all, it just knows aboutparsing and executing queries against a compiled schema.A companion library, lacinia-pedestal,is one way to expose your schema on the web.
Clients will typically send a JSON POST request, with a query
key containing the GraphQL query document:
{
hero {
id
name
}
}
The execute
function returns EDN data that can be easily converted to JSON.The :data key contains the value requested for the hero
query in the request.
{:data
{:hero {:id 2000
:name "Lando Calrissian"}}}
This example request has no errors, and contained only a single query.GraphQL supports multiple queries in a single request.There may be errors executing the query, Lacinia will process as much asit can, and will report errors in the :errors key.
One of the benefits of GraphQL is that the client has the power to renamefields in the response:
{
hero(episode: NEWHOPE) {
movies: appears_in
}
}
{:data {:hero {:movies [:NEWHOPE :EMPIRE :JEDI]}}}
This is just an overview, far more detail is availablein the manual.
Although this library is used in production at Walmart, it isstill considered beta software - subject to change.We hope to get to a 1.0 release in the reasonable future.
To use this library with Clojure 1.8, you must includea dependency on clojure-future-spec.
More details are in the manual.
Copyright © 2017-2021 WalmartLabs
Distributed under the Apache License, Version 2.0.
Nulla porttitor accumsan tincidunt. Nulla quis lorem ut libero malesuada feugiat. Vestibulum ac diam sit amet coc private server quam vehicula elementum sed sit amet dui. Praesent sapien massa, conval
Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque clashofclanshack nec, egestas non nisi. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Donec