Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQL API in Clojure

GraphQL API in Clojure

Introduction to GraphQL API development in Clojure

Kent OHASHI

January 31, 2019
Tweet

More Decks by Kent OHASHI

Other Decks in Programming

Transcript

  1. lagénorhynque lagénorhynque (defprofile lagénorhynque :aliases [ ] :languages [Clojure Common-Lisp

    Scheme Haskell English français] :interests [programming Love-Live! language-learning/linguistics law mathematics] :committing [github.com/lagenorhynque/duct.module.pedestal] :contributing [github.com/japan-clojurians/clojure-site-ja])
  2. 1. GraphQL GraphQL query GraphQL schema 2. API implementation project

    structure API server GraphQL implementation
  3. example example : AqoursQL, an example GraphQL API based on

    Lacinia-Pedestal & Duct lagenorhynque/aqoursql
  4. response (JSON) response (JSON) { "data": { "member_by_id": { "id":

    1, "name": " ", "organization_id": 1, "organization_name": " " } } }
  5. Clojure REPL Clojure REPL dev> (q #:venia{:queries [[:member_by_id {:id 1}

    [:id :name :organization_id :organization_name]]]}) {:data {:member_by_id {:id 1, :name " ", :organization_id 1, :organization_name " "}}}
  6. utility function utility function q q (defn q ([query] (q

    query nil)) ([query variables] (lacinia/execute (:aqoursql.graphql/schema system) (venia/graphql-query query) variables {:db (:duct.database.sql/hikaricp system)}))) venia.core/graphql­query com.walmartlabs.lacinia/execute
  7. response (JSON) response (JSON) { "data": { "songs": [ {

    "name": " ", "artist": { "name": "Aqours", "members": [ { "name": " " }, ... ] } }, ... ] }
  8. Clojure REPL Clojure REPL dev> (q #:venia{:queries [[:songs [:name [:artist

    [:name [:members [:name]]]]]]]}) {:data {:songs ({:name " ", :artist {:name "Aqours", :members ({:name " "} ...
  9. object de nition: object de nition: Member Member """ """

    type Member { """ ID""" id: Int! """ """ name: String! """ ID""" organization_id: Int! """ """ organization_name: String! }
  10. resources/aqoursql/graphql-schema.edn {:objects {:Member {:description " " :fields {:id {:type (non-null

    Int) :description " ID"} :name {:type (non-null String) :description " "} :organization_id {:type (non-null Int) :description " ID"} :organization_name {:type (non-null String) :description " "}}}}}
  11. object de nition: object de nition: Song Song """ """

    type Song { """ ID""" id: Int! """ """ name: String! """" ID"" artist_id: Int! """ """ artist: Artist! """ (YYYY-MM-DD)""" release_date: String! }
  12. resources/aqoursql/graphql-schema.edn {:objects {:Song {:description " " :fields {:id {:type (non-null

    Int) :description " ID"} :name {:type (non-null String) :description " "} :artist_id {:type (non-null Int) :description " ID"} :artist {:type (non-null :Artist) :description " "} :release_date {:type (non-null String) :description " (YYYY-MM-DD)"}}}}}
  13. query de nition: query de nition: member_by_id member_by_id type Query

    { """ID """ member_by_id( " ID" id: Int! ): Member }
  14. resources/aqoursql/graphql-schema.edn {:queries {:member_by_id {:type :Member :description "ID " :args {:id

    {:type (non-null Int) :description " ID"}} :resolve :query/member-by-id}}}
  15. query de nition: query de nition: songs songs type Query

    { """ """ songs( " ( )" name: String ): [Song] }
  16. {:queries {:songs {:type (list :Song) :description " " :args {:name

    {:type String :description " ( )"}} :resolve :query/songs}}}
  17. resources/aqoursql/con g.edn {:duct.profile/base {:duct.core/project-ns aqoursql :duct.server/pedestal { ... } :aqoursql.graphql/schema

    {} :aqoursql.graphql/service { ... }} :duct.profile/dev #duct/include "dev" :duct.profile/test #duct/include "test" :duct.profile/local #duct/include "local" :duct.profile/prod {} :duct.module/logging {} :duct.module/sql { ... } :duct.module/pedestal {}}
  18. resources/aqoursql/con g.edn {:duct.profile/base {:duct.core/project-ns aqoursql :duct.server/pedestal {:base-service #ig/ref :aqoursql.graphql/service :service

    #:io.pedestal.http{:join? true :host #duct/env "SERVER_HOST" :port #duct/env ["SERVER_PORT" Int :or 8888]}} ... } ... :duct.module/pedestal {}}
  19. resources/aqoursql/con g.edn {:duct.profile/base {:duct.core/project-ns aqoursql ... :aqoursql.graphql/schema {} :aqoursql.graphql/service {:schema

    #ig/ref :aqoursql.graphql/schema :options {:graphiql true :app-context {:db #ig/ref :duct.database/sql} :env :prod}}} ... }
  20. src/aqoursql/graphql.clj (defmethod ig/init-key ::schema [_ _] (-> (io/resource "aqoursql/graphql-schema.edn") slurp

    edn/read-string (util/attach-resolvers resolver-map) schema/compile)) (defmethod ig/init-key ::service [_ {:keys [schema options]}] (lacinia/service-map schema options))
  21. resolver (function) resolver (function) src/aqoursql/graphql.clj (def resolver-map {:query/artist-by-id artists/fetch-artist-by-id :query/artists

    artists/list-artists :query/member-by-id members/fetch-member-by-id :query/members members/list-members :query/song-by-id songs/fetch-song-by-id :query/songs songs/list-songs})
  22. resolver function spec src/aqoursql/resolver/members.clj (defn fetch-member-by-id [{:keys [db]} {:keys [id]}

    _] (db.member/find-member-by-id db id)) (s/fdef resolver :args (s/cat :app-context map? :arguments (s/nilable map?) :resovled-value (s/nilable map?)))
  23. boundary (DB) boundary (DB) src/aqoursql/boundary/db/member.clj (s/def ::id nat-int?) (s/def ::name

    string?) (s/def ::organization_id ::organization/id) (s/def ::organization_name ::organization/name) (s/def ::artist_id ::artist/id) (s/def ::artist_ids (s/coll-of ::artist/id)) (s/def ::member (s/keys :req-un [::id ::name ::organization_id] :opt-un [::organization_name ::artist_id]))
  24. (s/fdef find-member-by-id :args (s/cat :db ::db/db :id ::id) :ret (s/nilable

    ::member)) ... (defprotocol Member (find-member-by-id [db id]) ... )
  25. dev> (aqoursql.boundary.db.member/find-member-by-id (:duct.database.sql/hikaricp system) 1) {:id 1, :name " ",

    :organization_id 1, :organization_name " "} dev> (aqoursql.resolver.members/fetch-member-by-id {:db (:duct.database.sql/hikaricp system)} {:id 1} nil) {:id 1, :name " ", :organization_id 1, :organization_name " "} dev> (q #:venia{:queries [[:member_by_id {:id 1} [:name :organization_name]]]}) {:data {:member_by_id {:name " ", :organization_name " "}}}
  26. Further Reading Further Reading Lacinia Lacinia : Expose Lacinia GraphQL

    as Pedestal endpoints example: Lacinia Lacinia-Pedestal Clojure Lacinia GraphQL API lagenorhynque/aqoursql Clojure GraphQL Lacinia Tips
  27. GraphQL GraphQL : Clojure(Script) graphql query generation venia GraphQL |

    A query language for your API How to GraphQL - The Fullstack Tutorial for GraphQL GraphQL ─ REST API Learning GraphQL