Service Implementation¶
At this point, we’ve discussed what goes into each implementing service’s schema, and a bit about how each service is responsible for resolving representations; let’s finally see how this all fits together with Lacinia.
Below is a sketch of how this comes together in the products service:
(ns products.server
(:require
[io.pedestal.http :as http]
[clojure.java.io :as io]
[com.walmartlabs.lacinia.pedestal2 :as lp]
[com.walmartlabs.lacinia.parser.schema :refer [parse-schema]]
[com.walmartlabs.lacinia.schema :as schema]
[com.walmartlabs.lacinia.util :as util]))
(defn resolve-users-external
[_ _ reps]
(for [{:keys [id]} reps]
(schema/tag-with-type {:id id}
:User)))
(defn get-product-by-upc
[context upc]
;; Peform DB query here, return map with :upc, :name, :price
)
(defn get-favorite-products-for-user
[context user-id]
;; Perform DB query here, return seq of maps with :upc, :name, :price
)
(defn resolve-products-internal
[context _ reps]
(for [{:keys [upc]} reps
:let [product (get-product-by-upc context upc)]]
(schema/tag-with-type product :Product)))
(defn resolve-product-by-upc
[context {:keys [upc]} _]
(get-product-by-upc context upc))
(defn resolve-favorite-products
[context _ user]
(let [{:keys [id]} user]
(get-favorite-products-for-user context id)))
(defn products-schema
[]
(-> "products.gql"
io/resource
slurp
(parse-schema {:federation {:entity-resolvers {:Product resolve-products-internal
:User resolve-users-external}}})
(util/inject-resolvers {:Query/productByUpc #'resolve-product-by-upc
:User/favoriteProducts #'resolve-favorite-products})
schema/compile))
(defn start
[]
(-> (products-schema)
lp/default-service
http/create-server
http/start))
The resolve-users-external
function is used to convert a seq of User
representations
into a seq of User
entity stubs; this is called from the resolver for the _entities
query whose type
is a list of the _Entities
union, therefore each value must be tagged with the :User
type.
resolve-products-internal
does the same for Product
representations, but since this is the
products service, the expected behavior is to perform a query against an external data store and
ensure the results match the structure of the Product
entity.
resolve-product-by-upc
is the resolver function for the productByUpc
query.
Since the field type is Product
there’s no need to tag the value.
resolve-favorite-products
is the resolver function for the User/favoriteProducts
field.
This is passed the User
(provided by resolve-users-external
); it extracts the id
and passes
it to get-favorite-products-for-user
.
The remainder is bare-bones scaffolding to read, parse, and compile the schema and build a Pedestal service endpoint around it.
Pay careful attention to the call to com.walmartlabs.lacinia.parser-schema/parse-schema; the presence of the
:federation
option is critical; this adds the necessary base types and directives
before parsing the schema definition, and then adds the _entities
query and _Entities
union
afterwards, among other things.
The :entity-resolvers
map is critical; this maps from a type name to a entity resolver;
this information is used to build the field resolver function for the _entities
query.
Warning
A lot of details are left out of this, such as initializing the database and storing
the database connection into the application context, where functions like
get-product-by-upc
can access it.
This is only a sketch to help you connect the dots.