当前位置: 首页 > 软件库 > 大数据 > 数据查询 >

graphql-builder

授权协议 MIT License
开发语言 Java
所属分类 大数据、 数据查询
软件类型 开源软件
地区 不详
投 递 者 关冠宇
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

graphql-builder

GraphQL client library for Clojure and ClojureScript.

Why

Writing GraphQL queries in the frontend applications is not straight forward. In JavaScript world it is common to see GraphQL queries written as inline strings inside the application code:

client.query(`
    {
      allFilms {
        films {
          title
        }
      }
    }
`).then(result => {
    console.log(result.allFilms);
});

Although it gets the work done, it is easy to make mistakes without syntax coloring, and any validation of the query syntax is impossible. In ClojureScript this approach looks even worse:

(def inline-fragment-source "
query LoadStarships($starshipCount: Int!) {
  allStarships(first: $starshipCount) {
    edges {
      node {
        id
        name
        model
        costInCredits
        pilotConnection {
          edges {
            node {
              ...pilotFragment
            }
          }
        }
      }
    }
  }
}
fragment pilotFragment on Person {
  name
  homeworld { name }
}
")

I wanted something similar to the HugSQL library which would allow me to keep the queries inside the .graphql files while being able to easily use them from my frontend code.

Approach

This library uses the parser from the alumbra library to parse the .graphql files and then implements the GraphQL code generation on top of the output format.

Parsing and regenerating allows for some (automatic) advanced features:

  • Resolving dependencies between queries and fragments
  • Fragment inlining
  • Query namespacing (with prefixes)
  • Query composition - combine multiple queries into one query
  • Mutation composition – combine multiple mutations into one query
  • Subscriptions

API

Loading GraphQL files:

(ns graphql-test
    (:require
        [graphql-builder.parser :refer-macros [defgraphql]]
        [graphql-builder.core :as core]))

(defgraphql graphql-queries "file1.graphql" "file2.graphql")
(def query-map (core/query-map graphql-queries))

If the GraphQL file contained the following:

query LoadStarships($starshipCount: Int!) {
  allStarships(first: $starshipCount) {
    edges {
      node {
        id
        name
        model
        costInCredits
        pilotConnection {
          edges {
            node {
              ...pilotFragment
            }
          }
        }
      }
    }
  }
}
fragment pilotFragment on Person {
  name
  homeworld {
    name
  }
}

you could access the LoadStarships function like this:

(def load-starships-query (get-in query-map [:query :load-starships]))

The returned function accepts one argument: query variables (if needed). Calling the function will return the following:

(load-starships-query {})

;; return value from the load-starships-query function
{:graphql {:query "GraphQL Query string"
           :variables {...} ;; variables passed to the load-starships-query function
           :operationName "..." ;; Name of the query
           }
 :unpack (fn [data])} ;; function used to unpack the data returned from the GraphQL query

The returned GraphQL Query will contain all of the referenced fragments.

Calling the GraphQL API is out of the scope of this library, but it can be easily implemented with any of the ClojureScript AJAX Libraries.

Fragment Inlining

graphql-builder can inline the referenced fragments inside the query. To inline the fragments, pass the {:inline-fragments true} config to the query-map function:

(ns graphql-test
    (:require
        [graphql-builder.parser :refer-macros [defgraphql]]
        [graphql-builder.core :as core]))

(defgraphql graphq-queries "file1.graphql" "file2.graphql")
(def query-map (core/query-map graphql-queries {:inline-fragments true}))

If you called the load-starships-query function again, the returned GraphQL string would look like this:

query LoadStarships($starshipCount: Int!) {
  allStarships(first: $starshipCount) {
    edges {
      node {
        id
        name
        model
        costInCredits
        pilotConnection {
          edges {
            node {
              name
              homeworld {
                name
              }
            }
          }
        }
      }
    }
  }
}

Query prefixing (namespacing)

grapqhl-builder can "namespace" the GraphQL query. To namespace the query, pass the {:prefix "NameSpace"} config to the query-map function:

(ns graphql-test
    (:require
        [graphql-builder.parser :refer-macros [defgraphql]]
        [graphql-builder.core :as core]))

(defgraphql graphq-queries "file1.graphql" "file2.graphql")
(def query-map (core/query-map graphql-queries {:prefix "NameSpace"}))

If you called the load-starships-query function again, the returned GraphQL string would look like this:

query LoadStarships($NameSpace__starshipCount: Int!) {
  NameSpace__allStarships: allStarships(first: $NameSpace__starshipCount) {
    edges {
      node {
        id
        name
        model
        costInCredits
        pilotConnection {
          edges {
            node {
              ...pilotFragment
            }
          }
        }
      }
    }
  }
}

If the referenced fragments use variables, you must inline them to get the correct behavior.

Query Composition

Fragment inlining and namespacing are cool features on their own, but together they unlock the possibility to compose the queries.

Let's say that you have GraphQL file that contains the following query:

query Hero($episode: String!) {
  hero(episode: $episode) {
    name
  }
}

and you want to call the query for multiple episodes. Usually you would create another query for this:

query {
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

but, with graphql-builder you can compose this query from the application code:

(def composed-query
   (core/composed-query graphql-queries {:jedi-hero "Hero" :empire-hero "Hero"}))

Now you can call this function and it will handle namespacing both of the query and the variables automatically:

(composed-query {:empire-hero {:episode "EMPIRE"}} {:jedi-hero {:episode "JEDI"}})

This function will return the same object like the functions created by the query-map:

;; return value from the load-starships-query function
{:graphql {:query "GraphQL Query string"
           :variables {...} ;; variables passed to the load-starships-query function
           :operationName "..." ;; Name of the query
           }
 :unpack (fn [data])} ;; function used to unpack the data returned from the GraphQL query

In this case the GraphQL query string will look like this:

query ComposedQuery($JediHero__episode: String!, $EmpireHero__episode: String!) {
  JediHero__hero: hero(episode: $JediHero__episode) {
    name
  }
  EmpireHero__hero: hero(episode: $EmpireHero__episode) {
    name
  }
}

When you receive the result, you can use the returned unpack function to unpack them.

(unpack {"EmpireHero__hero" {:name "Foo"} "JediHero__hero" {:name "Bar"}})

;; This will return the unpacked results:

{:empire-hero {"hero" "Foo"}
 :jedi-hero {"hero" "Bar"}}

Mutation Composition

You can also compose mutations in the same manner you can compose queries. The onlydifference is that the mutations might depend on each other, so the ordering of thosemutations might be relevant.

This can be achieved by providing mutation keys that are sorted by the sort method inClojure.

Assuming you have a mutation

mutation AddStarship($name: String!){
    addStarship(name: $name){
        id
   }
}

You can compose multiple mutations together using the composed-mutation function:

(def composed-mutation
   (core/composed-mutation graphql-queries {:add-starship-1 "AddStarship"
                                            :add-starship-2 "AddStarship"}))

When you execute the result, you get back the same structure as with composed queries,providing unpack function to parse the result from the server.

(let [{unpack :unpack} (composed-mutation)]
  (unpack {"AddStarship1__name" "starship-1"
           "AddStarship2__name" "starship-2"}})

returns

{:add-starship-1 {"name" "starship-1"}
 :add-starship-2 {"name" "starship-2"}}

Tests

License

Copyright Mihael Konjevic, Tibor Kranjcec (konjevic@gmail.com) © 2020

Distributed under the MIT license.

  • 入门 graphql-java 需要运行于 Java 8 或更高版本. 如何在 Gradle 中使用最新正式版本 首先,保证 mavenCentral 在你的 repos 库列表中: repositories { mavenCentral() } 依赖: dependencies { compile 'com.graphql-java:graphql-java:6.0' } 如果在 Maven 中

  • 一、前言 GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 基于node的服务端开发中,GraphQL技术较为成熟常用,在基于java的服务端开发中,由于国内对该API

  • 入门 graphql-java 需要运行于 Java 8 或更高版本. 如何在 Gradle 中使用最新正式版本 首先,保证 mavenCentral 在你的 repos 库列表中: repositories { mavenCentral() } 依赖: dependencies { compile 'com.graphql-java:graphql-java:6.0' } 如果在 Maven 中

  • Schema 创建一个schema GraphQL API具有一个Schema,该Schema定义了可以Query(查询)或Mutation(变更)的每个字段以及这些字段的类型。 graphql-java提供了两种不同的定义schema的方式:编程方式编写,和使用graphql dsl语法(也称为SDL)编写。 例如: SDL示例: type Foo { bar: Stri

  • https://dreamylost.cn/ 文档 version = 1.4 仅供参考 Instrumentation 这个词没找到好的中文对应。以下直接使用本单词。 graphql.execution.instrumentation.Instrumentation接口允许您注入代码,这可以观察一个查询的执行并且还可以更改运行时行为。 这样做的主要用例是支持性能监控和自定义日志记录,但是它也可以

  • 这次来说一下相对应 “增删改的” Mutation操作,也是使用对应的DataFetcher,只不过增加了一个增删改的操作后返回查询结果罢了,老样子来看一个例子,IDL方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 schema { mutation: userMutat

  • 在spring-boot中使用graphql 首先构建spring-boot项目,pom.xml文件中加入 com.graphql-java-kickstart graphql-java-tools 5.4.0 com.graphql-java graphql-spring-boot-starter 5.0.2 com.graphql-java graphiql-spring-boot-start

  • GraphQL实战-第三篇-spring Boot 实现 在上一篇的java实现中,分享了使用GraphQL的基本流程,接下来分享一下在Spring Boot中对GraphQL的应用 首先创建一个Spring Boot的项目 POM依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/PO

  • GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 基于node的服务端开发中,GraphQL技术较为成熟常用,在基于java的服务端开发中,由于国内对该API标准的了解

 相关资料
  • 快速开始 GraphQL 是一种用于 API 的查询语言。这是 GraphQL 和 REST 之间一个很好的比较 (译者注: GraphQL 替代 REST 是必然趋势)。在这组文章中, 我们不会解释什幺是 GraphQL, 而是演示如何使用 @nestjs/GraphQL 模块。 GraphQLModule 只不过是 Apollo 服务器的包装器。我们没有造轮子, 而是提供一个现成的模块, 这让

  • GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 向你的 API 发出一个 GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测

  • Graphql editor 是一款 Graphql 的可视化编辑器和 IDE,帮助用户更容易理解 GraphQL 模式,通过使用可视化块系统创建模式。GraphQL Editor 将把它们转化为代码。通过 GraphQL Editor,用户可以在不写任何代码的情况下创建可视化的图表,或者以一种很好的方式呈现其模式。 GraphQL View Code Editor View Hierarchy View

  • GraphQL CLI Help us to improve new GraphQL CLI. Check out the new structure and commands below!Feel free to contact us in Discord channel. We would love to hear your feedback. Features Helpful command

  • Fullstack GraphQL Simple Demo Application API built with Node + Express + GraphQL + Sequelize (supports MySQL, Postgres, Sqlite and MSSQL). WebApp built with React + Redux. Written in ES6 using Babel

  • Hasura GraphQL Engine Hasura is an open source product that accelerates API development by 10x by giving you GraphQL or REST APIs with built in authorization on your data, instantly. Read more at hasu