├── docs
├── .nvmrc
├── static
│ └── _redirects
├── .gitignore
├── package.json
├── gatsby-config.js
└── source
│ ├── index.mdx
│ ├── essentials
│ ├── installation.md
│ ├── getting-started.md
│ ├── mutations.md
│ └── queries.md
│ └── advanced
│ └── fragments.md
├── project
├── build.properties
└── plugins.sbt
├── .gitignore
├── example
├── opt-launcher.js
├── public
│ ├── favicon.ico
│ ├── manifest.json
│ ├── index.html
│ └── index-fastopt.html
├── hot-launcher.js
├── src
│ └── main
│ │ ├── graphql
│ │ └── todo.graphql
│ │ └── scala
│ │ └── com
│ │ └── apollographql
│ │ └── scalajs
│ │ ├── TodosView.scala
│ │ ├── Main.scala
│ │ └── AddTodo.scala
├── README.md
├── webpack-opt.config.js
├── webpack-fastopt.config.js
├── build.sbt
└── schema.json
├── tests
├── src
│ └── test
│ │ ├── graphql
│ │ ├── mutations
│ │ │ ├── todos.graphql
│ │ │ ├── addtodo.graphql
│ │ │ └── apollo.config.js
│ │ └── queries
│ │ │ ├── currencyrates.graphql
│ │ │ ├── usdrates.graphql
│ │ │ └── apollo.config.js
│ │ └── scala
│ │ └── com
│ │ └── apollographql
│ │ └── scalajs
│ │ ├── ApolloLinkTest.scala
│ │ ├── react
│ │ ├── ReactApolloTest.scala
│ │ ├── UseMutationTest.scala
│ │ ├── UseQueryTest.scala
│ │ ├── QueryComponentTest.scala
│ │ └── MutationComponentTest.scala
│ │ └── ApolloClientTest.scala
├── build.sbt
├── queries.json
└── mutations.json
├── renovate.json
├── .gitmodules
├── core
├── build.sbt
└── src
│ └── main
│ └── scala
│ └── com
│ └── apollographql
│ └── scalajs
│ ├── cache
│ ├── ApolloCache.scala
│ ├── package.scala
│ └── InMemoryCache.scala
│ ├── GraphQLQuery.scala
│ ├── GraphQLTag.scala
│ ├── link
│ ├── package.scala
│ ├── HttpLink.scala
│ ├── Observable.scala
│ └── ApolloLink.scala
│ ├── OptionalResult.scala
│ └── ApolloClient.scala
├── react
├── build.sbt
└── src
│ └── main
│ └── scala
│ └── com
│ └── apollographql
│ └── scalajs
│ └── react
│ ├── ApolloProvider.scala
│ ├── ReactApollo.scala
│ ├── Mutation.scala
│ ├── Hooks.scala
│ └── Query.scala
├── netlify.toml
├── .travis.yml
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitleaks.toml
├── LICENSE
└── README.md
/docs/.nvmrc:
--------------------------------------------------------------------------------
1 | 11
2 |
--------------------------------------------------------------------------------
/docs/static/_redirects:
--------------------------------------------------------------------------------
1 | / /docs/scalajs/
2 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.3.6
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | target/
3 | project/project/
4 | .idea/
5 | node_modules/
6 |
--------------------------------------------------------------------------------
/example/opt-launcher.js:
--------------------------------------------------------------------------------
1 | require("./react-apollo-scalajs-example-opt.js").main();
2 |
--------------------------------------------------------------------------------
/tests/src/test/graphql/mutations/todos.graphql:
--------------------------------------------------------------------------------
1 | query AllTodosId {
2 | todos {
3 | id
4 | }
5 | }
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-scalajs/HEAD/example/public/favicon.ico
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 | db.json
4 | *.log
5 | node_modules/
6 | public/*
7 | .cache
8 | .deploy*/
9 | docs.json
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "packageFiles": [
3 | "docs/package.json"
4 | ],
5 | "extends": [
6 | "apollo-docs"
7 | ]
8 | }
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "docs/themes/meteor"]
2 | path = docs/themes/meteor
3 | url = https://github.com/meteor/hexo-theme-meteor.git
4 |
--------------------------------------------------------------------------------
/tests/src/test/graphql/mutations/addtodo.graphql:
--------------------------------------------------------------------------------
1 | mutation AddTodo($typ: String!) {
2 | addTodo(type: $typ) {
3 | typ: type
4 | }
5 | }
--------------------------------------------------------------------------------
/example/hot-launcher.js:
--------------------------------------------------------------------------------
1 | require("./react-apollo-scalajs-example-fastopt.js").main();
2 |
3 | if (module.hot) {
4 | module.hot.accept();
5 | }
6 |
--------------------------------------------------------------------------------
/tests/src/test/graphql/queries/currencyrates.graphql:
--------------------------------------------------------------------------------
1 | query CurrencyRates($cur: String!) {
2 | rates(currency: $cur) {
3 | currency
4 | }
5 | }
--------------------------------------------------------------------------------
/core/build.sbt:
--------------------------------------------------------------------------------
1 | enablePlugins(ScalaJSPlugin)
2 |
3 | name := "apollo-scalajs-core"
4 |
5 | libraryDependencies += "me.shadaj" %%% "slinky-readwrite" % "0.6.5"
6 |
--------------------------------------------------------------------------------
/react/build.sbt:
--------------------------------------------------------------------------------
1 | enablePlugins(ScalaJSPlugin)
2 |
3 | name := "apollo-scalajs-react"
4 |
5 | libraryDependencies += "me.shadaj" %%% "slinky-core" % "0.6.5"
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/cache/ApolloCache.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.cache
2 |
3 | import scala.scalajs.js
4 |
5 | @js.native
6 | trait ApolloCache extends js.Object
7 |
--------------------------------------------------------------------------------
/tests/src/test/graphql/queries/usdrates.graphql:
--------------------------------------------------------------------------------
1 | fragment CurrencyFragment on ExchangeRate {
2 | currency
3 | }
4 |
5 | query USDRates {
6 | rates(currency: "USD") {
7 | ...CurrencyFragment
8 | }
9 | }
--------------------------------------------------------------------------------
/example/src/main/graphql/todo.graphql:
--------------------------------------------------------------------------------
1 | query AllTodos {
2 | todos {
3 | id
4 | typ: type
5 | }
6 | }
7 |
8 | mutation AddTodo($typ: String!) {
9 | addTodo(type: $typ) {
10 | id
11 | typ: type
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/src/test/graphql/mutations/apollo.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | client: {
3 | service: {
4 | name: "test-schema",
5 | localSchemaFile: "tests/mutations.json"
6 | },
7 | includes: [ "*.graphql" ]
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/tests/src/test/graphql/queries/apollo.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | client: {
3 | service: {
4 | name: "test-schema",
5 | localSchemaFile: "tests/queries.json"
6 | },
7 | includes: [ "*.graphql" ]
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/cache/package.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scala.scalajs.js
4 |
5 | package object cache {
6 | type CacheResolver = js.Function3[js.Any, js.Dictionary[js.Any], CacheResolverContext, js.Any]
7 | }
8 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.2.0")
2 |
3 | addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.18.0")
4 |
5 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.6")
6 |
7 | addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0")
8 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | base = "docs/"
3 | publish = "docs/public/"
4 | command = "gatsby build --prefix-paths && mkdir -p docs/scalajs && mv public/* docs/scalajs && mv docs public/ && mv public/docs/scalajs/_redirects public"
5 | [build.environment]
6 | NPM_VERSION = "6"
7 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "prestart": "gatsby clean",
4 | "start": "gatsby develop"
5 | },
6 | "dependencies": {
7 | "gatsby": "^2.21.0",
8 | "gatsby-theme-apollo-docs": "^4.2.0",
9 | "react": "^16.9.0",
10 | "react-dom": "^16.9.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | branches:
3 | only:
4 | - master
5 | - /^v\d+(\.\d+)+$/
6 | install:
7 | - . $HOME/.nvm/nvm.sh
8 | - nvm install stable
9 | - npm install -g apollo@2.31.0
10 | script:
11 | - sbt +tests/test
12 | - sbt ";set scalaJSStage in Global := FullOptStage; +tests/test"
13 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/GraphQLQuery.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | trait GraphQLQuery {
4 | val operation: DocumentNode
5 |
6 | type Data
7 | type Variables
8 | }
9 |
10 | trait GraphQLMutation {
11 | val operation: DocumentNode
12 |
13 | type Data
14 | type Variables
15 | }
16 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/GraphQLTag.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.annotation.JSImport
5 |
6 | @js.native
7 | trait DocumentNode extends js.Object
8 |
9 | @js.native
10 | @JSImport("@apollo/client", "gql")
11 | object gql extends js.Object {
12 | def apply(query: String): DocumentNode = js.native
13 | }
14 |
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # react-apollo-scalajs example
2 | This is a simple example demonstrating the features of `react-apollo-scalajs`, such as performing queries and mutations with variables.
3 |
4 | To launch the example, start SBT in the root directory and start the Webpack Dev Server
5 |
6 | ```bash
7 | sbt example/fastOptJS::startWebpackDevServer
8 | ```
9 |
10 | Open up http://localhost:8080/webpack-dev-server and select the main bundle to test out the example!
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/link/package.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.|
5 |
6 | package object link {
7 | type Subscriber[T] = js.Function1[SubscriptionObserver[T], Unit | js.Function0[Unit] | Subscription]
8 |
9 | type NextLink = js.Function1[Operation, Observable[FetchResult]]
10 | type RequestHandler = js.Function2[Operation, js.UndefOr[NextLink], Observable[FetchResult]]
11 | }
12 |
--------------------------------------------------------------------------------
/.gitleaks.toml:
--------------------------------------------------------------------------------
1 |
2 | [[ rules ]]
3 | id = "high-entropy-base64"
4 | [ rules.allowlist ]
5 | commits = [
6 | "5926b555e4866ebe8daf80408adbd5743ef2a2ab",
7 |
8 | ]
9 |
10 | [[ rules ]]
11 | id = "generic-api-key"
12 | [ rules.allowlist ]
13 | commits = [
14 | # The same secret as was showing up above appeared in a differnet signature
15 | # This is a defunct API key for algolia docsearch
16 | "5926b555e4866ebe8daf80408adbd5743ef2a2ab"
17 | ]
18 |
--------------------------------------------------------------------------------
/react/src/main/scala/com/apollographql/scalajs/react/ApolloProvider.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import scala.scalajs.js
4 | import slinky.core.ExternalComponent
5 | import slinky.core.annotations.react
6 | import scala.scalajs.js.annotation.JSImport
7 | import com.apollographql.scalajs.ApolloClient
8 |
9 | @react object ApolloProvider extends ExternalComponent {
10 | case class Props(client: ApolloClient)
11 |
12 | @js.native
13 | @JSImport("@apollo/client", "ApolloProvider")
14 | val apolloProviderComponent: js.Object = js.native
15 |
16 | override val component = apolloProviderComponent
17 | }
18 |
--------------------------------------------------------------------------------
/react/src/main/scala/com/apollographql/scalajs/react/ReactApollo.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import slinky.core.facade.ReactElement
4 |
5 | import scala.scalajs.js
6 | import scala.scalajs.js.annotation.JSImport
7 |
8 | @js.native
9 | @JSImport("@apollo/client/react/components", JSImport.Namespace)
10 | object ReactApollo extends js.Object {
11 | val Query: js.Object = js.native
12 | val Mutation: js.Object = js.native
13 | }
14 |
15 | @js.native
16 | @JSImport("@apollo/client/react/ssr", JSImport.Namespace)
17 | object ReactApolloServer extends js.Object {
18 | def renderToStringWithData(component: ReactElement): js.Promise[String] = js.native
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/link/HttpLink.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.link
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.annotation.JSImport
5 |
6 | trait HttpLinkOptions extends js.Object {
7 | val uri: String
8 | val fetch: js.UndefOr[js.Object] = js.undefined
9 | }
10 |
11 | object HttpLinkOptions {
12 | def apply(uri: String, fetch: js.UndefOr[js.Object] = js.undefined): HttpLinkOptions = {
13 | js.Dynamic.literal(
14 | uri = uri,
15 | fetch = fetch
16 | ).asInstanceOf[HttpLinkOptions]
17 | }
18 | }
19 |
20 | @JSImport("@apollo/client", "HttpLink")
21 | @js.native
22 | class HttpLink(options: HttpLinkOptions) extends ApolloLink
23 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/OptionalResult.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scala.scalajs.js
4 | import scala.language.implicitConversions
5 |
6 | @js.native trait OptionalValue[T] extends js.Object
7 | object OptionalValue {
8 | implicit class OptionalValueExt[T](private val optionalValue: OptionalValue[T]) extends AnyVal {
9 | def toOption: Option[T] = Option(optionalValue.asInstanceOf[T])
10 | }
11 |
12 | final def empty[T]: OptionalValue[T] = null.asInstanceOf[OptionalValue[T]]
13 | implicit def toOption[T](v: OptionalValue[T]): Option[T] = v.toOption
14 | implicit def fromValue[T](v: T): OptionalValue[T] = v.asInstanceOf[OptionalValue[T]]
15 | implicit def fromOption[T](v: Option[T])(implicit ev: Null <:< T): OptionalValue[T] = v.orNull.asInstanceOf[OptionalValue[T]]
16 | }
17 |
--------------------------------------------------------------------------------
/docs/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const themeOptions = require('gatsby-theme-apollo-docs/theme-options');
2 |
3 | module.exports = {
4 | pathPrefix: '/docs/scalajs',
5 | plugins: [
6 | {
7 | resolve: 'gatsby-theme-apollo-docs',
8 | options: {
9 | ...themeOptions,
10 | root: __dirname,
11 | subtitle: 'Apollo Scala.js',
12 | description: 'Use Apollo Client from your Scala.js applications',
13 | githubRepo: 'apollographql/apollo-scalajs',
14 | sidebarCategories: {
15 | null: [
16 | 'index'
17 | ],
18 | Essentials: [
19 | 'essentials/installation',
20 | 'essentials/getting-started',
21 | 'essentials/queries',
22 | 'essentials/mutations'
23 | ],
24 | Advanced: [
25 | 'advanced/fragments'
26 | ]
27 | }
28 | }
29 | }
30 | ]
31 | };
32 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/ApolloLinkTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import com.apollographql.scalajs.link.{ApolloLink, GraphQLRequest, HttpLink, HttpLinkOptions}
4 | import org.scalatest.{Assertion, AsyncFunSuite}
5 |
6 | import scala.concurrent.Promise
7 | import scala.scalajs.js
8 |
9 | class ApolloLinkTest extends AsyncFunSuite {
10 | js.Dynamic.global.window.fetch = UnfetchFetch
11 |
12 | implicit override def executionContext =
13 | scala.concurrent.ExecutionContext.Implicits.global
14 |
15 | test("Can perform a query with an HttpLink") {
16 | val resultPromise = Promise[Assertion]
17 | ApolloLink.execute(
18 | new HttpLink(HttpLinkOptions("https://graphql-currency-rates.glitch.me")),
19 | GraphQLRequest(
20 | gql(
21 | """{
22 | | rates(currency: "USD") {
23 | | currency
24 | | }
25 | |}""".stripMargin
26 | )
27 | )
28 | ).forEach { res =>
29 | resultPromise.success(assert(true))
30 | }
31 |
32 | resultPromise.future
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/link/Observable.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.link
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.annotation.JSImport
5 |
6 | @js.native
7 | trait SubscriptionObserver[T] extends js.Object {
8 | var closed: Boolean = js.native
9 | def next(value: T): Unit = js.native
10 | def error(errorValue: js.Any): Unit = js.native
11 | def complete(): Unit = js.native
12 | }
13 |
14 | @js.native
15 | trait Subscription extends js.Object {
16 | var closed: Boolean = js.native
17 | def unsubscribe(): Unit = js.native
18 | }
19 |
20 | @JSImport("apollo-link", "Observable")
21 | @js.native
22 | class Observable[T](subscriber: Subscriber[T]) extends js.Object {
23 | def subscribe(observerOrNext: js.Function1[T, Unit],
24 | error: js.UndefOr[js.Function1[T, Unit]] = js.undefined,
25 | complete: js.UndefOr[js.Function0[Unit]] = js.undefined): Subscription = js.native
26 | def subscribe(observer: SubscriptionObserver[T]): Subscription = js.native
27 |
28 | def forEach(fn: js.Function1[T, Unit]): js.Promise[Unit] = js.native
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Meteor Development Group
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/src/main/scala/com/apollographql/scalajs/TodosView.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import com.apollographql.scalajs.react.Query
4 | import slinky.core.StatelessComponent
5 | import slinky.core.annotations.react
6 | import slinky.core.facade.ReactElement
7 | import slinky.web.html._
8 | import slinky.core.facade.Fragment
9 | import slinky.core.facade.Hooks._
10 | import slinky.core.SyntheticEvent
11 | import org.scalajs.dom.{html, Event}
12 |
13 | @react class TodosView extends StatelessComponent {
14 | type Props = Unit
15 |
16 | def render(): ReactElement = {
17 | Fragment(
18 | h1("Todos"),
19 | Query(AllTodosQuery) { res =>
20 | res.data.map { d =>
21 | div(
22 | d.todos.toList.flatMap { todos =>
23 | todos.toList.flatMap { todo =>
24 | todo.map { todo =>
25 | div(key := todo.id.toString)(
26 | h1(todo.typ),
27 | )
28 | }
29 | }
30 | }
31 | ): ReactElement
32 | }.getOrElse(h1("loading!"))
33 | },
34 | AddTodo()
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/cache/InMemoryCache.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.cache
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.annotation.JSImport
5 |
6 | @js.native
7 | trait CacheResolverContext extends js.Object {
8 | def getCacheKey(value: js.Object): js.Object = js.native
9 | }
10 |
11 | trait InMemoryCacheConfig {
12 | val resultCaching: js.UndefOr[Boolean]
13 | val possibleTypes: js.UndefOr[js.Dynamic]
14 | val typePolicies: js.UndefOr[js.Dynamic]
15 | }
16 |
17 | object InMemoryCacheConfig {
18 | def apply(
19 | resultCaching: js.UndefOr[Boolean] = js.undefined,
20 | possibleTypes: js.UndefOr[js.Dynamic] = js.undefined,
21 | typePolicies: js.UndefOr[js.Dynamic] = js.undefined
22 | ): InMemoryCacheConfig = {
23 | js.Dynamic.literal(
24 | resultCaching = resultCaching,
25 | possibleTypes = possibleTypes,
26 | typePolicies = typePolicies
27 | ).asInstanceOf[InMemoryCacheConfig]
28 | }
29 | }
30 |
31 | @JSImport("@apollo/client/cache", "InMemoryCache")
32 | @js.native
33 | class InMemoryCache(options: js.UndefOr[InMemoryCacheConfig] = js.undefined) extends ApolloCache
34 |
--------------------------------------------------------------------------------
/example/src/main/scala/com/apollographql/scalajs/Main.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import com.apollographql.scalajs.react.ApolloProvider
4 | import slinky.web.ReactDOM
5 | import slinky.web.html._
6 | import slinky.hot
7 | import org.scalajs.dom.{document, html}
8 |
9 | import scala.scalajs.js
10 | import scala.scalajs.js.annotation.JSExportTopLevel
11 | import scala.scalajs.LinkingInfo
12 | import com.apollographql.scalajs.cache.InMemoryCache
13 |
14 | object Main {
15 | @JSExportTopLevel("main")
16 | def main(): Unit = {
17 | if (LinkingInfo.developmentMode) {
18 | hot.initialize()
19 | }
20 |
21 | if (js.typeOf(js.Dynamic.global.window.reactContainer) == "undefined") {
22 | js.Dynamic.global.window.reactContainer = document.createElement("div")
23 | document.body.appendChild(js.Dynamic.global.window.reactContainer.asInstanceOf[html.Element])
24 | }
25 |
26 | val client = new ApolloClient(
27 | ApolloClientOptions(
28 | uri = "https://graphql-todo-tracker.glitch.me",
29 | cache = new InMemoryCache()
30 | )
31 | )
32 |
33 | ReactDOM.render(
34 | ApolloProvider(client)(
35 | TodosView()
36 | ),
37 | js.Dynamic.global.window.reactContainer.asInstanceOf[html.Element]
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example/webpack-opt.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var path = require("path");
3 |
4 | var HtmlWebpackPlugin = require('html-webpack-plugin');
5 | var CopyWebpackPlugin = require('copy-webpack-plugin');
6 |
7 | module.exports = {
8 | "entry": {
9 | "react-apollo-scalajs-example-opt": [ path.resolve(__dirname, "./opt-launcher.js") ]
10 | },
11 | "output": {
12 | "path": path.resolve(__dirname, "../../../../build"),
13 | "filename": "[name]-bundle.js"
14 | },
15 | resolve: {
16 | alias: {
17 | "resources": path.resolve(__dirname, "../../../../src/main/resources")
18 | }
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.css$/,
24 | use: [ 'style-loader', 'css-loader' ]
25 | },
26 | // "file" loader for svg
27 | {
28 | test: /\.svg$/,
29 | use: [
30 | {
31 | loader: 'file-loader',
32 | query: {
33 | name: 'static/media/[name].[hash:8].[ext]'
34 | }
35 | }
36 | ]
37 | }
38 | ]
39 | },
40 | plugins: [
41 | new CopyWebpackPlugin([
42 | { from: path.resolve(__dirname, "../../../../public") }
43 | ]),
44 | new HtmlWebpackPlugin({
45 | template: path.resolve(__dirname, "../../../../public/index.html")
46 | }),
47 | new webpack.DefinePlugin({
48 | 'process.env': {
49 | NODE_ENV: JSON.stringify('production')
50 | }
51 | }),
52 | new webpack.optimize.UglifyJsPlugin()
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/example/webpack-fastopt.config.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 | var HtmlWebpackPlugin = require('html-webpack-plugin');
3 | var CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: {
7 | "react-apollo-scalajs-example-fastopt": ["./react-apollo-scalajs-example-fastopt-entrypoint.js"],
8 | "launcher": ["./hot-launcher.js"]
9 | },
10 | output: {
11 | path: __dirname,
12 | filename: "[name]-library.js",
13 | library: "appLibrary",
14 | libraryTarget: "var"
15 | },
16 | devtool: "source-map",
17 | resolve: {
18 | alias: {
19 | "resources": path.resolve(__dirname, "../../../../src/main/resources")
20 | }
21 | },
22 | module: {
23 | rules: [
24 | {
25 | test: /\.css$/,
26 | use: [ 'style-loader', 'css-loader' ]
27 | },
28 | // "file" loader for svg
29 | {
30 | test: /\.svg$/,
31 | use: [
32 | {
33 | loader: 'file-loader',
34 | query: {
35 | name: 'static/media/[name].[hash:8].[ext]'
36 | }
37 | }
38 | ]
39 | }
40 | ],
41 | noParse: (content) => {
42 | return content.endsWith("-fastopt.js");
43 | }
44 | },
45 | plugins: [
46 | new CopyWebpackPlugin([
47 | { from: path.resolve(__dirname, "../../../../public") }
48 | ]),
49 | new HtmlWebpackPlugin({
50 | template: path.resolve(__dirname, "../../../../public/index-fastopt.html"),
51 | inject: false
52 | })
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/docs/source/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: ''
4 | ---
5 |
6 | Apollo Scala.js is a strongly-typed interface for using Apollo Client and React Apollo from Scala.js applications. With Apollo Scala.js, you can elegantly implement Scala components that pull data with GraphQL and use the data in a typesafe manner. Combined with Apollo CLI, you can write queries separately and automatically get static types to handle GraphQL data in your application.
7 |
8 | Apollo Scala.js offers both a low-level interface for direct control over how GraphQL results are handled and integration with React Apollo through Slinky for embedding GraphQL in React applications. Just like Apollo Client, Apollo Scala.js also supports React Native, so you can take GraphQL wherever you go.
9 |
10 | ## Examples
11 | For a simple application demonstrating how to perform queries and mutations with React Apollo in a Scala.js app, take a look at the [example](https://github.com/apollographql/react-apollo-scalajs/tree/master/example) folder.
12 |
13 | ## Additional Resources
14 | + If you're using the React API, take a look at the docs for [Slinky](https://slinky.shadaj.me), which Apollo Scala.js uses to provide access to the React Apollo's API.
15 | + [Apollo CLI](https://github.com/apollographql/apollo-cli) has built-in support for generating Scala types from GraphQL queries that are compatible with Apollo Scala.js
16 | + The official [Apollo Client docs](https://www.apollographql.com/docs/react/) provide full details on what you can do with Apollo Client, which Apollo Scala.js is built on
17 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/src/main/scala/com/apollographql/scalajs/AddTodo.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scalajs.js
4 | import com.apollographql.scalajs.react.Query
5 | import slinky.core._
6 | import slinky.core.annotations.react
7 | import slinky.core.facade._
8 | import slinky.web.html._
9 | import slinky.core.facade.Hooks._
10 | import org.scalajs.dom.{html, Event}
11 | import com.apollographql.scalajs.react.Mutation
12 | import com.apollographql.scalajs.react.UpdateStrategy
13 | import com.apollographql.scalajs.react._
14 | import com.apollographql.scalajs.react.HooksApi._
15 |
16 | @react object AddTodo {
17 | type Props = Unit
18 |
19 | val component = FunctionalComponent[Props] { props =>
20 | val (todoText, setTodoText) = useState("")
21 |
22 | val (todoMut, res) = {
23 | val hook = useMutation[AddTodoMutation.type](
24 | AddTodoMutation.operation,
25 | MutationHookOptions[AddTodoMutation.type](
26 | refetchQueries = js.Array("AllTodos".asInstanceOf[js.Dynamic])
27 | )
28 | )
29 |
30 | (
31 | (variables: AddTodoMutation.Variables) => {
32 | hook._1.apply(MutationHookOptions[AddTodoMutation.type](variables = variables)).toFuture
33 | },
34 | hook._2
35 | )
36 | }
37 |
38 | div(
39 | input(value := todoText, onChange := ((e) => setTodoText(e.target.value))),
40 | button(
41 | if (res.loading) "Adding" else "Add Todo",
42 | onClick := ((_) => {
43 | todoMut(AddTodoMutation.Variables(todoText))
44 | setTodoText("")
45 | }),
46 | disabled := res.loading || todoText.trim.isEmpty)
47 | )
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/link/ApolloLink.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.link
2 |
3 | import com.apollographql.scalajs.DocumentNode
4 |
5 | import scala.scalajs.js
6 | import scala.scalajs.js.annotation.JSImport
7 | import scala.scalajs.js.|
8 |
9 | @js.native
10 | trait GraphQLRequest extends js.Object {
11 | val query: DocumentNode = js.native
12 | }
13 |
14 | object GraphQLRequest {
15 | def apply(query: DocumentNode): GraphQLRequest = {
16 | js.Dynamic.literal(
17 | query = query
18 | ).asInstanceOf[GraphQLRequest]
19 | }
20 | }
21 |
22 | @js.native
23 | trait FetchResult extends js.Object
24 |
25 | @js.native
26 | trait Operation extends js.Object {
27 | def setContext(context: js.Dictionary[js.Any]): js.Dictionary[js.Any] = js.native
28 | def getContext(): js.Dictionary[js.Any] = js.native
29 | def toKey(): String = js.native
30 | }
31 |
32 | @JSImport("@apollo/client", "ApolloLink")
33 | @js.native
34 | class ApolloLink(handler: js.UndefOr[js.Function2[Operation, js.UndefOr[NextLink], Observable[FetchResult]]] = js.undefined) extends js.Object {
35 | def concat(next: ApolloLink | RequestHandler): ApolloLink = js.native
36 | def request(operation: Operation, forward: js.UndefOr[NextLink]): Observable[FetchResult] = js.native
37 | }
38 |
39 | @JSImport("@apollo/client", "ApolloLink")
40 | @js.native
41 | object ApolloLink extends js.Object {
42 | def empty(): ApolloLink = js.native
43 | def from(links: js.Array[ApolloLink]): ApolloLink = js.native
44 | def concat(first: ApolloLink, second: ApolloLink): ApolloLink = js.native
45 | def execute(link: ApolloLink, operation: GraphQLRequest): Observable[FetchResult] = js.native
46 | }
47 |
--------------------------------------------------------------------------------
/tests/build.sbt:
--------------------------------------------------------------------------------
1 | enablePlugins(ScalaJSBundlerPlugin)
2 |
3 | libraryDependencies += "org.scalatest" %%% "scalatest" % "3.1.1" % Test
4 | libraryDependencies += "me.shadaj" %%% "slinky-web" % "0.6.6" % Test
5 |
6 | npmDependencies in Compile += "@apollo/client" -> "3.2.4"
7 |
8 | npmDevDependencies in Compile += "apollo" -> "2.31.0"
9 |
10 | Test / npmDependencies += "react" -> "16.8.4"
11 | Test / npmDependencies += "react-dom" -> "16.8.4"
12 |
13 | Compile / npmDependencies += "unfetch" -> "2.1.1"
14 |
15 | Test / requireJsDomEnv := true
16 |
17 | val namespace = "com.apollographql.scalajs"
18 |
19 | (Test / sourceGenerators) += Def.task {
20 | import scala.sys.process._
21 |
22 | val out = (Test / sourceManaged).value
23 |
24 | out.mkdirs()
25 |
26 | val graphQLScala = out / "queries.scala"
27 |
28 | Seq(
29 | "apollo", "client:codegen",
30 | "--config", "tests/src/test/graphql/queries/apollo.config.js",
31 | "--target", "scala",
32 | "--namespace", namespace, graphQLScala.getAbsolutePath
33 | ).!
34 |
35 | Seq(graphQLScala)
36 | }
37 |
38 | (Test / sourceGenerators) += Def.task {
39 | import scala.sys.process._
40 |
41 | val out = (Test / sourceManaged).value
42 |
43 | out.mkdirs()
44 |
45 | val graphQLScala = out / "mutations.scala"
46 |
47 | Seq(
48 | "apollo", "client:codegen",
49 | "--config", "tests/src/test/graphql/mutations/apollo.config.js",
50 | "--target", "scala",
51 | "--namespace", namespace, graphQLScala.getAbsolutePath
52 | ).!
53 |
54 | Seq(graphQLScala)
55 | }
56 |
57 | Test / watchSources ++= ((Test / sourceDirectory).value / "graphql" ** "*.graphql").get
58 |
59 | scalaJSLinkerConfig ~= { _.withESFeatures(_.withUseECMAScript2015(false)) }
60 |
--------------------------------------------------------------------------------
/example/public/index-fastopt.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/build.sbt:
--------------------------------------------------------------------------------
1 | enablePlugins(ScalaJSBundlerPlugin)
2 |
3 | name := "react-apollo-scalajs-example"
4 |
5 | libraryDependencies += "me.shadaj" %%% "slinky-web" % "0.6.6"
6 | libraryDependencies += "me.shadaj" %%% "slinky-hot" % "0.6.6"
7 |
8 | npmDependencies in Compile += "react" -> "16.14.0"
9 | npmDependencies in Compile += "react-dom" -> "16.14.0"
10 | npmDependencies in Compile += "react-proxy" -> "1.1.8"
11 |
12 | npmDependencies in Compile += "@apollo/client" -> "3.2.4"
13 |
14 | npmDevDependencies in Compile += "apollo" -> "2.31.0"
15 | npmDevDependencies in Compile += "file-loader" -> "1.1.5"
16 | npmDevDependencies in Compile += "style-loader" -> "0.19.0"
17 | npmDevDependencies in Compile += "css-loader" -> "0.28.7"
18 | npmDevDependencies in Compile += "html-webpack-plugin" -> "4.3.0"
19 | npmDevDependencies in Compile += "copy-webpack-plugin" -> "5.1.1"
20 |
21 | webpackConfigFile in fastOptJS := Some(baseDirectory.value / "webpack-fastopt.config.js")
22 | webpackConfigFile in fullOptJS := Some(baseDirectory.value / "webpack-opt.config.js")
23 |
24 | webpackDevServerExtraArgs := Seq("--inline", "--hot")
25 |
26 | webpackBundlingMode in fastOptJS := BundlingMode.LibraryOnly()
27 |
28 |
29 | val namespace = "com.apollographql.scalajs"
30 |
31 | (sourceGenerators in Compile) += Def.task {
32 | import scala.sys.process._
33 |
34 | val out = (sourceManaged in Compile).value
35 |
36 | out.mkdirs()
37 |
38 | val graphQLScala = out / "graphql.scala"
39 |
40 | Seq(
41 | "apollo", "client:codegen", s"--queries=${((sourceDirectory in Compile).value / "graphql").getAbsolutePath}/*.graphql",
42 | s"--localSchemaFile=${(baseDirectory.value / "schema.json").getAbsolutePath}",
43 | "--target=scala",
44 | s"--namespace=$namespace",
45 | graphQLScala.getAbsolutePath
46 | ).!
47 |
48 | Seq(graphQLScala)
49 | }
50 |
51 | watchSources ++= ((sourceDirectory in Compile).value / "graphql" ** "*.graphql").get
52 |
--------------------------------------------------------------------------------
/docs/source/essentials/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | order: 0
4 | ---
5 |
6 | Add the dependency to your `build.sbt`
7 |
8 | ```scala
9 | resolvers += "Apollo Bintray" at "https://dl.bintray.com/apollographql/maven/"
10 | libraryDependencies += "com.apollographql" %%% "apollo-scalajs-core" % "0.8.0"
11 | libraryDependencies += "com.apollographql" %%% "apollo-scalajs-react" % "0.8.0"
12 | ```
13 |
14 | If you are using the React API, you may want to add other Slinky modules such as the web and hot-reloading module, so check out the instructions at https://slinky.shadaj.me/docs/installation/.
15 |
16 | ## NPM Dependencies
17 | If you are using [Scala.js Bundler](https://scalacenter.github.io/scalajs-bundler/), you will need to add the Apollo Client dependencies to your `build.sbt`.
18 |
19 | ```scala
20 | npmDependencies in Compile += "@apollo/client" -> "3.2.4"
21 | ```
22 |
23 | ## Apollo CLI
24 | To set up the code generator, which generates static types based on your GraphQL queries, first install the Apollo CLI.
25 |
26 | ```bash
27 | npm install -g apollo
28 | ```
29 |
30 | Then, you can configure SBT to automatically run it and add the resulting Scala sources to your build.
31 |
32 | ```scala
33 | val namespace = "com.my.package.graphql"
34 |
35 | (sourceGenerators in Compile) += Def.task {
36 | import scala.sys.process._
37 |
38 | val out = (sourceManaged in Compile).value
39 |
40 | out.mkdirs()
41 |
42 | Seq(
43 | "apollo", "client:codegen",
44 | "--config", "apollo.config.js",
45 | "--target", "scala",
46 | "--namespace", namespace, graphQLScala.getAbsolutePath
47 | ).!
48 |
49 | Seq(out / "graphql.scala")
50 | }
51 |
52 | watchSources ++= ((sourceDirectory in Compile).value / "graphql" ** "*.graphql").get
53 | ```
54 |
55 | With the accompanying [`apollo.config.js`](https://www.apollographql.com/docs/references/apollo-config.html):
56 |
57 | ```js
58 | module.exports = {
59 | client: {
60 | service: {
61 | name: "test-schema",
62 | localSchemaFile: "schema.json"
63 | },
64 | includes: [ "src/main/graphql/*.graphql" ]
65 | }
66 | };
67 | ```
68 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/react/ReactApolloTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs.cache.InMemoryCache
4 | import com.apollographql.scalajs.link.{HttpLink, HttpLinkOptions}
5 | import com.apollographql.scalajs.{ApolloClient, CurrencyRatesQuery, UnfetchFetch}
6 | import org.scalajs.dom.document
7 | import org.scalatest.funsuite.AsyncFunSuite
8 | import org.scalatest.Assertion
9 | import slinky.web.ReactDOM
10 | import slinky.web.html.div
11 |
12 | import scala.concurrent.Promise
13 | import scala.scalajs.js
14 | import scala.scalajs.js.JSON
15 | import com.apollographql.scalajs.ApolloClientOptions
16 |
17 | class ReactApolloTest extends AsyncFunSuite {
18 | js.Dynamic.global.window.fetch = UnfetchFetch
19 |
20 | implicit override def executionContext =
21 | scala.concurrent.ExecutionContext.Implicits.global
22 |
23 | test("Can mount an ApolloProvider with a client instance") {
24 | assert(!js.isUndefined(
25 | ReactDOM.render(
26 | ApolloProvider(
27 | client = new ApolloClient(
28 | ApolloClientOptions(
29 | uri = "https://graphql-currency-rates.glitch.me",
30 | cache = new InMemoryCache()
31 | )
32 | )
33 | )(
34 | div()
35 | ),
36 | document.createElement("div")
37 | )
38 | ))
39 | }
40 |
41 | test("Can server-side render data to string based on a query") {
42 | val link = new HttpLink(options = HttpLinkOptions(uri = "https://graphql-currency-rates.glitch.me"))
43 | val cache = new InMemoryCache()
44 | val client = new ApolloClient(options = ApolloClientOptions(ssrMode = true, link = link, cache = cache))
45 |
46 | ReactApolloServer.renderToStringWithData(
47 | ApolloProvider(ApolloProvider.Props(client = client))(
48 | Query(
49 | CurrencyRatesQuery,
50 | CurrencyRatesQuery.Variables("USD")
51 | ) { d =>
52 | if (d.data.isDefined) {
53 | div(d.data.get.rates.get.head.get.currency.get)
54 | } else ""
55 | }
56 | )
57 | ).toFuture.map { html =>
58 | assert(html == """AED
""")
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/react/UseMutationTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs._
4 | import org.scalajs.dom.document
5 | import org.scalatest.funsuite.AsyncFunSuite
6 | import org.scalatest.Assertion
7 | import slinky.web.ReactDOM
8 | import slinky.web.html.div
9 | import slinky.core.annotations.react
10 | import slinky.core.facade.Hooks._
11 |
12 | import scala.concurrent.Promise
13 | import scala.scalajs.js
14 | import com.apollographql.scalajs.cache.InMemoryCache
15 | import com.apollographql.scalajs.react.HooksApi._
16 | import slinky.core.FunctionalComponent
17 |
18 | class UseMutationTest extends AsyncFunSuite {
19 | js.Dynamic.global.window.fetch = UnfetchFetch
20 |
21 | implicit override def executionContext =
22 | scala.concurrent.ExecutionContext.Implicits.global
23 |
24 | test("Can make simple mutation") {
25 | val gotDataPromise = Promise[Assertion]
26 |
27 | @react object UseMutationTestComponent {
28 | case class Props()
29 |
30 | val component = FunctionalComponent[Props] { _ =>
31 | val (mut, result) = {
32 | val hook = useMutation[AddTodoMutation.type](
33 | AddTodoMutation.operation
34 | )
35 |
36 | (
37 | (variables: AddTodoMutation.Variables) => {
38 | hook._1.apply(MutationHookOptions[AddTodoMutation.type](variables = variables)).toFuture
39 | },
40 | hook._2
41 | )
42 | }
43 |
44 | mut(AddTodoMutation.Variables(typ = "test"))
45 |
46 | useEffect(() => {
47 | if (result.data.isDefined) {
48 | gotDataPromise.success(assert(result.data.get.addTodo.nonEmpty))
49 | }
50 | }, Seq(result))
51 |
52 | div()
53 | }
54 | }
55 |
56 | ReactDOM.render(
57 | ApolloProvider(
58 | client = new ApolloClient(
59 | ApolloClientOptions(
60 | uri = "https://graphql-todo-tracker.glitch.me",
61 | cache = new InMemoryCache()
62 | )
63 | )
64 | )(
65 | UseMutationTestComponent()
66 | ),
67 | document.createElement("div")
68 | )
69 |
70 | gotDataPromise.future
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/docs/source/advanced/fragments.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using Fragments
3 | description: ''
4 | ---
5 |
6 | Fragments are a powerful tool in statically-typed languages, allowing you to share a common model type across multiple queries in your application. Apollo Scala.js handles fragment types through implicit conversions between the original data type generated for a specific query and the fragment type that contains a specific subset of fields.
7 |
8 | ## Generating types for fragments
9 | The Apollo CLI handles fragments just like any other GraphQL definition. Because all your GraphQL files are merged before code generation runs, fragments can be defined in their own file and referenced from other queries in your project.
10 |
11 | Let's take a look at the code generation output for a simple query that references a fragment:
12 |
13 | ```graphql
14 | fragment CurrencyFragment on ExchangeRate {
15 | currency
16 | }
17 |
18 | query USDRatesWithFragment {
19 | rates(currency: "USD") {
20 | ...CurrencyFragment
21 | }
22 | }
23 | ```
24 |
25 | This will result in the following type definitions, with a query-specific type `UsdRatesWithFragmentQuery.Data.Rate` but also a general fragment type `CurrencyFragment`:
26 |
27 | ```scala
28 | object UsdRatesWithFragmentQuery extends com.apollographql.scalajs.GraphQLQuery {
29 | // ...
30 |
31 | case class Data(rates: Option[Seq[Option[Data.Rate]]]) {
32 | }
33 |
34 | object Data {
35 | val possibleTypes = scala.collection.Set("Query")
36 |
37 | case class Rate(currency: Option[String]) {
38 | }
39 |
40 | object Rate {
41 | val possibleTypes = scala.collection.Set("ExchangeRate")
42 | implicit def toCurrencyFragment(a: Rate): CurrencyFragment = CurrencyFragment(a.currency)
43 | }
44 | }
45 | }
46 |
47 | case class CurrencyFragment(currency: Option[String]) {
48 | }
49 | ```
50 |
51 | ## Applying fragments in application code
52 |
53 | If we have components of our application that will be recieving currency data from different queries, we could then use the `CurrencyFragment` type to recieve that data.
54 |
55 | ```scala
56 | def myGeneralCurrencyCode(currencyData: CurrencyFragment) =
57 | currencyData.currency.getOrElse("Currency not known")
58 |
59 | // ...
60 |
61 | Query(UsdRatesWithFragmentQuery) { d =>
62 | if (d.data.isDefined) {
63 | div({
64 | for {
65 | rates <- d.data.get.rates // Seq[Option[Data.Rate]]
66 | maybeRate <- rates // Option[Data.Rate]
67 | rate <- maybeRate // Data.Rate
68 | } yield div(myGeneralCurrencyCode(rate /* converted to CurrencyFragment here */))
69 | })
70 | gotDataPromise.success(assert(d.data.get.rates.exists(_.nonEmpty)))
71 | } else {
72 | div("Loading!")
73 | }
74 | }
75 | ```
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Archival
2 | This repo was archived by the Apollo Security team on 2023-05-26
3 |
4 |
5 | # Apollo Scala.js
6 | _use Apollo Client and React Apollo from your Scala.js apps!_
7 |
8 | ## View the [docs](https://www.apollographql.com/docs/scalajs)
9 |
10 | ## Installation
11 | Add the dependency to your build.sbt
12 | ```scala
13 | resolvers += "Apollo Bintray" at "https://dl.bintray.com/apollographql/maven/"
14 | libraryDependencies += "com.apollographql" %%% "apollo-scalajs-core" % "0.8.0" // if you are writing a vanilla Scala.js app
15 | libraryDependencies += "com.apollographql" %%% "apollo-scalajs-react" % "0.8.0" // if you are writing a React Scala.js app
16 | ```
17 |
18 | You probably also want to add other Slinky modules such as the web module, so check out the instructions at https://slinky.dev
19 |
20 | To set up the code generator, which uses the Apollo CLI to generate static types for your GraphQL queries, first install `apollo`
21 | ```npm i -g apollo```
22 |
23 | and then set up SBT to automatically run it
24 |
25 | ```scala
26 | val namespace = "your package here"
27 |
28 | (sourceGenerators in Compile) += Def.task {
29 | import scala.sys.process._
30 |
31 | val out = (sourceManaged in Compile).value
32 |
33 | out.mkdirs()
34 |
35 | Seq(
36 | "apollo", "client:codegen", s"--queries=${((sourceDirectory in Compile).value / "graphql").getAbsolutePath}/*.graphql",
37 | s"--localSchemaFile=${(baseDirectory.value / "schema.json").getAbsolutePath}",
38 | "--target=scala",
39 | s"--namespace=$namespace",
40 | graphQLScala.getAbsolutePath
41 | ).!
42 |
43 | Seq(out / "graphql.scala")
44 | }
45 | ```
46 |
47 | ## Usage
48 | Once you have placed some GraphQL queries in `src/main/graphql`, you can use the generated types to create GraphQL-powered React components!
49 |
50 | To integrate GraphQL data in your React tree, simply use the `Query` component to render subtrees based on a specified query.
51 |
52 | ```scala
53 | Query(UsdRatesQuery) { queryStatus =>
54 | if (queryStatus.loading) "Loading..."
55 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
56 | else {
57 | div(queryStatus.data.get.rates.mkString(", "))
58 | }
59 | }
60 | ```
61 |
62 | For more on implementing advanced components, follow the instructions at https://slinky.shadaj.me
63 |
64 | Next, to initialize Apollo Client in your application, first create an instance of the client (here using Apollo Boost)
65 |
66 | ```scala
67 | val client = ApolloBoostClient(
68 | uri = "https://graphql-currency-rates.glitch.me"
69 | )
70 | ```
71 |
72 | Finally, wrap your React component tree inside an `ApolloProvider` component, which all components inside to perform GraphQL queries with the specified client
73 |
74 | ```scala
75 | ApolloProvider(client)(
76 | ...
77 | )
78 | ```
79 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/apollographql/scalajs/ApolloClient.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import scala.concurrent.{ExecutionContext, Future}
4 | import scala.scalajs.js
5 | import scala.scalajs.js.annotation.JSImport
6 |
7 | @js.native
8 | trait ApolloClientOptions extends js.Object {
9 | val uri: js.UndefOr[String] = js.native
10 | val link: js.UndefOr[js.Object] = js.native
11 | val cache: js.UndefOr[js.Object] = js.native
12 | val name: js.UndefOr[String] = js.native
13 | val version: js.UndefOr[String] = js.native
14 | val ssrMode: js.UndefOr[Boolean] = js.native
15 | val ssrForceFetchDelay: js.UndefOr[Int] = js.native
16 | val connectToDevTools: js.UndefOr[Boolean] = js.native
17 | val queryDeduplication: js.UndefOr[Boolean] = js.native
18 | val defaultOptions: js.UndefOr[js.Dynamic] = js.native
19 | }
20 |
21 | object ApolloClientOptions {
22 | def apply(
23 | uri: js.UndefOr[String] = js.undefined,
24 | link: js.UndefOr[js.Object] = js.undefined,
25 | cache: js.UndefOr[js.Object] = js.undefined,
26 | name: js.UndefOr[String] = js.undefined,
27 | version: js.UndefOr[String] = js.undefined,
28 | ssrMode: js.UndefOr[Boolean] = js.undefined,
29 | ssrForceFetchDelay: js.UndefOr[Int] = js.undefined,
30 | connectToDevTools: js.UndefOr[Boolean] = js.undefined,
31 | queryDeduplication: js.UndefOr[Boolean] = js.undefined,
32 | defaultOptions: js.UndefOr[js.Dynamic] = js.undefined
33 | ): ApolloClientOptions = {
34 | js.Dynamic.literal(
35 | uri = uri,
36 | link = link,
37 | cache = cache,
38 | name = name,
39 | version = version,
40 | ssrMode = ssrMode,
41 | ssrForceFetchDelay = ssrForceFetchDelay,
42 | connectToDevTools = connectToDevTools,
43 | queryDeduplication = queryDeduplication,
44 | defaultOptions = defaultOptions
45 | ).asInstanceOf[ApolloClientOptions]
46 | }
47 | }
48 |
49 | @JSImport("@apollo/client", "ApolloClient")
50 | @js.native
51 | class ApolloClient(options: ApolloClientOptions) extends js.Object {
52 | def query[D <: js.Object, V <: js.Any](options: QueryOptions[V]): js.Promise[ApolloQueryResult[D]] = js.native
53 | }
54 |
55 | @js.native
56 | trait QueryOptions[V] extends js.Object {
57 | val query: DocumentNode = js.native
58 | val variables: js.UndefOr[V] = js.native
59 | }
60 |
61 | object QueryOptions {
62 | def apply[V](
63 | query: DocumentNode,
64 | variables: js.UndefOr[V] = js.undefined
65 | ) = {
66 | js.Dynamic.literal(
67 | query = query,
68 | variables = variables.asInstanceOf[js.Object]
69 | ).asInstanceOf[QueryOptions[V]]
70 | }
71 | }
72 |
73 | @js.native
74 | trait ApolloQueryResult[D] extends js.Object {
75 | val data: js.UndefOr[D]
76 | val errors: js.UndefOr[js.Array[js.Dynamic]]
77 | val error: js.UndefOr[js.Dynamic]
78 | val loading: Boolean
79 | val networkStatus: Int
80 | val partial: js.UndefOr[Boolean]
81 | }
82 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/react/UseQueryTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs._
4 | import org.scalajs.dom.document
5 | import org.scalatest.funsuite.AsyncFunSuite
6 | import org.scalatest.Assertion
7 | import slinky.web.ReactDOM
8 | import slinky.web.html.div
9 | import slinky.core.annotations.react
10 | import slinky.core.facade.Hooks._
11 |
12 | import scala.concurrent.Promise
13 | import scala.scalajs.js
14 | import com.apollographql.scalajs.cache.InMemoryCache
15 | import com.apollographql.scalajs.react.HooksApi._
16 | import slinky.core.FunctionalComponent
17 |
18 | class UseQueryTest extends AsyncFunSuite {
19 | js.Dynamic.global.window.fetch = UnfetchFetch
20 |
21 | implicit override def executionContext =
22 | scala.concurrent.ExecutionContext.Implicits.global
23 |
24 | test("Can make a simple query") {
25 | val gotDataPromise = Promise[Assertion]
26 |
27 | @react object UseQueryTestComponent {
28 | case class Props()
29 |
30 | val component = FunctionalComponent[Props] { _ =>
31 | val QueryResult(data, _, _) = useQuery[UsdRatesQuery.type](UsdRatesQuery.operation)
32 |
33 | useEffect(() => {
34 | if (data.isDefined) {
35 | gotDataPromise.success(assert(data.get.rates.nonEmpty))
36 | }
37 | }, Seq(data))
38 |
39 | div()
40 | }
41 | }
42 |
43 | ReactDOM.render(
44 | ApolloProvider(
45 | client = new ApolloClient(
46 | ApolloClientOptions(
47 | uri = "https://graphql-currency-rates.glitch.me",
48 | cache = new InMemoryCache()
49 | )
50 | )
51 | )(
52 | UseQueryTestComponent()
53 | ),
54 | document.createElement("div")
55 | )
56 |
57 | gotDataPromise.future
58 | }
59 |
60 | test("Can make a Query with variables") {
61 | val gotDataPromise = Promise[Assertion]
62 |
63 | @react object UseQueryTestComponent {
64 | case class Props()
65 |
66 | val component = FunctionalComponent[Props] { _ =>
67 | val QueryResult(data, _, _) = useQuery[CurrencyRatesQuery.type](
68 | CurrencyRatesQuery.operation,
69 | QueryHookOptions[CurrencyRatesQuery.type](variables = CurrencyRatesQuery.Variables("USD"))
70 | )
71 |
72 | useEffect(() => {
73 | if (data.isDefined) {
74 | gotDataPromise.success(assert(data.get.rates.nonEmpty))
75 | }
76 | }, Seq(data))
77 |
78 | div()
79 | }
80 | }
81 |
82 | ReactDOM.render(
83 | ApolloProvider(
84 | client = new ApolloClient(
85 | ApolloClientOptions(
86 | uri = "https://graphql-currency-rates.glitch.me",
87 | cache = new InMemoryCache()
88 | )
89 | )
90 | )(
91 | UseQueryTestComponent()
92 | ),
93 | document.createElement("div")
94 | )
95 |
96 | gotDataPromise.future
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/ApolloClientTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs
2 |
3 | import com.apollographql.scalajs.cache.InMemoryCache
4 | import com.apollographql.scalajs.link.{HttpLink, HttpLinkOptions}
5 | import org.scalatest.AsyncFunSuite
6 |
7 | import scala.scalajs.js
8 | import scala.scalajs.js.annotation.JSImport
9 |
10 | @js.native
11 | @JSImport("unfetch", JSImport.Default)
12 | object UnfetchFetch extends js.Object
13 |
14 | class ApolloClientTest extends AsyncFunSuite {
15 | js.Dynamic.global.window.fetch = UnfetchFetch
16 |
17 | implicit override def executionContext =
18 | scala.concurrent.ExecutionContext.Implicits.global
19 |
20 | test("Can create an instance of Apollo Client") {
21 | assert(!js.isUndefined(
22 | new ApolloClient(
23 | ApolloClientOptions(
24 | uri = "https://graphql-currency-rates.glitch.me",
25 | cache = new InMemoryCache()
26 | )
27 | )
28 | ))
29 | }
30 |
31 | test("Can perform a simple query and get the results") {
32 | new ApolloClient(
33 | ApolloClientOptions(
34 | uri = "https://graphql-currency-rates.glitch.me",
35 | cache = new InMemoryCache()
36 | )
37 | ).query[js.Object, js.Object](
38 | QueryOptions(
39 | gql(
40 | """{
41 | | rates(currency: "USD") {
42 | | currency
43 | | }
44 | |}""".stripMargin
45 | )
46 | )
47 | ).toFuture.map { r =>
48 | assert(r.data.asInstanceOf[js.Dynamic]
49 | .rates.asInstanceOf[js.Array[js.Object]].length > 0)
50 | }
51 | }
52 |
53 | test("Can perform a query with variables and get the results") {
54 | new ApolloClient(
55 | ApolloClientOptions(
56 | uri = "https://graphql-currency-rates.glitch.me",
57 | cache = new InMemoryCache()
58 | )
59 | ).query[js.Object, js.Object](
60 | QueryOptions(
61 | gql(
62 | """query GetRates($cur: String!) {
63 | | rates(currency: $cur) {
64 | | currency
65 | | }
66 | |}""".stripMargin
67 | ),
68 | variables = js.Dynamic.literal(
69 | cur = "USD"
70 | )
71 | )
72 | ).toFuture.map { r =>
73 | assert(r.data.asInstanceOf[js.Dynamic]
74 | .rates.asInstanceOf[js.Array[js.Object]].length > 0)
75 | }
76 | }
77 |
78 | test("Can perform a query with typed variables and response and get the results") {
79 | trait Variables extends js.Object {
80 | val cur: String
81 | }
82 |
83 | trait Rate extends js.Object{
84 | val currency: String
85 | }
86 |
87 | trait QueryResult extends js.Object {
88 | val rates: js.Array[Rate]
89 | }
90 |
91 | new ApolloClient(
92 | ApolloClientOptions(
93 | uri = "https://graphql-currency-rates.glitch.me",
94 | cache = new InMemoryCache()
95 | )
96 | ).query[QueryResult, Variables](
97 | QueryOptions(
98 | query = gql(
99 | """query GetRates($cur: String!) {
100 | | rates(currency: $cur) {
101 | | currency
102 | | }
103 | |}""".stripMargin
104 | ),
105 | variables = new Variables {
106 | override val cur = "USD"
107 | }
108 | )
109 | ).toFuture.map { r =>
110 | assert(r.data.get.rates.nonEmpty)
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/docs/source/essentials/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | order: 1
4 | ---
5 |
6 | Now that you have your build set up, let's start writing our first GraphQL application!
7 |
8 | ## Create a client
9 | First, we need to create an instance of Apollo Client, which will handle sending our GraphQL queries and caching the results (among many other features).
10 |
11 | Inside your main function, we can use Apollo Boost to set up a client with a default HTTP link and in-memory cache.
12 | ```scala
13 | import com.apollographql.scalajs._
14 |
15 | def main(): Unit = { // called when the app launches
16 | val client = ApolloBoostClient(
17 | uri = "https://graphql-currency-rates.glitch.me"
18 | )
19 | }
20 | ```
21 |
22 | That’s it! Now your client is ready to start fetching data. Before we hook up Apollo Scala.js to React, let’s try sending a query with plain Scala first with the `client.query` function.
23 |
24 | ```scala
25 | client.query[js.Object](gql( // gql is a member of the com.apollographql.scalajs package that parses your query
26 | """{
27 | | rates(currency: "USD") {
28 | | currency
29 | | }
30 | |}""".stripMargin
31 | ))).foreach { result =>
32 | println(result.data)
33 | }
34 | ```
35 |
36 | If you open up the console, you should see the result of your GraphQL query. Now, let's learn how to connect Apollo Scala.js to React so we can start building query components with React Apollo.
37 |
38 | ## Connecting your client to React
39 | To connect Apollo Client to React, you will need to use the `ApolloProvider` component, which can be found in the `com.apollographql.scalajs.react` package. For details on what the `ApolloProvider` component does, see the [React Apollo Docs](https://www.apollographql.com/docs/react/essentials/get-started/#creating-provider). We suggest placing your `ApolloProvider` somewhere high in your app, above any places where you need to access GraphQL data.
40 |
41 | ```scala
42 | import com.apollographql.scalajs.react.ApolloProvider
43 |
44 | ReactDOM.render(
45 | ApolloProvider(client)(
46 | ...
47 | ),
48 | rootElement
49 | )
50 | ```
51 |
52 | ## Request data
53 | Now that you have an `ApolloProvider` mounted, it's time to start performing queries with the `Query` component!
54 |
55 | First, specify the type of your query result, then pass in your `gql`-parsed GraphQL query and a function that determines what to render as a curried parameter. To keep things simple, let's leave our component untyped by specifying `js.Object` as the return type.
56 |
57 | The curried parameter is a function that takes the current query resolution result and returns a React tree. Because we don't always have the data requested, the `data` property on the result object is an `Option`.
58 |
59 | ```scala
60 | import com.apollographql.scalajs.react.Query
61 |
62 | Query[js.Object](gql(
63 | """{
64 | | rates(currency: "USD") {
65 | | currency
66 | | }
67 | |}""".stripMargin
68 | ))) { result =>
69 | if (result.loading) {
70 | h1("Loading!")
71 | } else {
72 | div(result.data.get.toString)
73 | }
74 | }
75 | ```
76 |
77 | If you include this component in your React tree, you should see your GraphQL query result rendered to your screen.
78 |
79 | ## Next steps
80 | Now that you’ve learned how to fetch data with Apollo Scala.js, you’re ready to dive deeper into creating more complex queries and mutations. After this section, we recommend moving onto:
81 | + [Queries](/essentials/queries/): Learn how to fetch queries with arguments and handle the results with static types
82 | + [Mutations](/essentials/mutations/): Learn how to update data with mutations and type your parameters and results
--------------------------------------------------------------------------------
/docs/source/essentials/mutations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mutations
3 | order: 3
4 | ---
5 |
6 | Now that you know how to get GraphQL data, it's time to update that data with GraphQL mutations!
7 |
8 | ## The Mutation component
9 | Using the Mutation component from Scala.js is just like using it with the original JavaScript API. Simply pass in a parsed GraphQL mutation object (from `gql`) and a function that decides when to perform the mutaiton what to render given the state of the mutation. Because the Mutation component tracks all steps of performing a mutation, you can easily render loading and error states by reading properties of the mutation status given to you.
10 |
11 | When you use the Mutation component, Apollo Client also keeps your local cache in sync with the server by updating the cache with the mutation response.
12 |
13 | Let's see this in action!
14 |
15 | ```scala
16 | import com.apollographql.scalajs.react.Mutation
17 |
18 | Mutation[js.Object, js.Object](
19 | gql(
20 | """mutation addTodo($type: String!) {
21 | | addTodo(type: $type) {
22 | | id
23 | | type
24 | | }
25 | |}""".stripMargin
26 | )
27 | ) { (addTodo, status) =>
28 | button(onClick := () => {
29 | addTodo(js.Dynamic.literal(
30 | "type" -> "myTodoType"
31 | ))
32 | })
33 | }
34 | ```
35 |
36 | Here, we specified the type of the variables and result to be `js.Object`, essentially making it untyped. This data isn't super friendly to work with, though, so let's see how we can type our data.
37 |
38 | ## Typing Mutation Variables and Responses
39 | Apollo Scala.js allows you to specify models for the variables and mutations results. These models are usually case classes and can be handwritten or generated by Apollo CLI (see [automatic mutation types](#automatic-mutation-types) for how to set this up).
40 |
41 | Let's say we have a mutation
42 | ```graphql
43 | mutation addTodo($todoType: String!) {
44 | addTodo(type: $todoType) {
45 | id
46 | }
47 | }
48 | ```
49 |
50 | The corresponding type for this would look something like this:
51 | ```scala
52 | case class Variables(todoType: String)
53 | case class Todo(id: String)
54 | case class MutationData(addTodo: Todo)
55 | ```
56 |
57 | Once we have these types written, all we have to do is replace the `js.Object`s with `MutationData` and `Variables` to get typed mutation calls and responses! Apollo Scala.js handles the process of generating JavaScript objects from your variables parsing the result into Scala objects, so you can focus on using the data.
58 |
59 | ```scala
60 | Mutation[MutationData, Variables](
61 | gql(
62 | """mutation addTodo($type: String!) {
63 | | addTodo(type: $type) {
64 | | id
65 | | type
66 | | }
67 | |}""".stripMargin
68 | )
69 | )) { (addTodo, mutationStatus) =>
70 | button(onClick := () => {
71 | addTodo(Variables(
72 | todoType = "myTodoType"
73 | ))
74 | })
75 | }
76 | ```
77 |
78 | ## Automatic Mutation Types
79 | With `apollo`, we can automatically generate mutation objects that tie together GraphQL mutations with variables and response types based on the schema definition. First, make sure you have followed the [Apollo CLI Installation](/essentials/installation/#apollo-cli) steps and downloaded a schema by following the [instructions](https://github.com/apollographql/apollo-cli#apollo-schemadownload-output). For this example, we will be using the GraphQL server at `https://graphql-todo-tracker.glitch.me`.
80 |
81 | With Apollo CLI installed, we can define our first mutation! Under `src/main/graphql`, you can define static mutation that will be converted to Scala code by Apollo CLI. For example, we could define a mutation `addtodo.graphql`:
82 | ```graphql
83 | mutation AddTodo($typ: String!) {
84 | addTodo(type: $typ) {
85 | typ: type
86 | }
87 | }
88 | ```
89 |
90 | Which results in an object `AddTodoMutation` being generated. To use this in a mutation component, we can simply pass the generated object in instead of a mutation string and Apollo Scala.js will automatically gather the variables and result type based on the generated code.
91 |
92 | ```scala
93 | Mutation(AddTodoMutation) { (addTodo, mutationStatus) =>
94 | button(onClick := () => {
95 | addTodo(AddTodoMutation.Variables(
96 | typ = "myTodoType"
97 | ))
98 | })
99 | }
100 | ```
101 |
--------------------------------------------------------------------------------
/react/src/main/scala/com/apollographql/scalajs/react/Mutation.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs._
4 | import slinky.core.ExternalComponent
5 | import slinky.core.facade.ReactElement
6 | import slinky.readwrite.{Reader, Writer}
7 |
8 | import scala.concurrent.Future
9 | import scala.language.implicitConversions
10 | import scala.scalajs.js
11 | import scala.scalajs.js.|
12 | import scala.scalajs.js.annotation.JSImport
13 |
14 | case class MutationData[T](loading: Boolean, called: Boolean, error: Option[js.Error], data: Option[T])
15 | object MutationData {
16 | implicit def reader[T](implicit tReader: Reader[T]): Reader[MutationData[T]] = { o =>
17 | val dyn = o.asInstanceOf[js.Dynamic]
18 | val loading = Reader.booleanReader.read(dyn.loading.asInstanceOf[js.Object])
19 | MutationData(
20 | loading,
21 | Reader.booleanReader.read(dyn.called.asInstanceOf[js.Object]),
22 | Reader.optionReader[js.Error].read(dyn.error.asInstanceOf[js.Object]),
23 | if (loading) None else Reader.optionReader(tReader).read(dyn.data.asInstanceOf[js.Object])
24 | )
25 | }
26 | }
27 |
28 | case class CallMutationProps[V](variables: V)
29 | object CallMutationProps {
30 | implicit def vToCall[V](value: V): CallMutationProps[V] = CallMutationProps(value)
31 | implicit def writer[T](implicit tWriter: Writer[T]): Writer[CallMutationProps[T]] = { v =>
32 | js.Dynamic.literal(
33 | variables = tWriter.write(v.variables)
34 | )
35 | }
36 | }
37 |
38 | case class UpdateStrategy(refetchQueries: Seq[String] = Seq())
39 | object Mutation extends ExternalComponent {
40 | case class Props(mutation: DocumentNode,
41 | refetchQueries: Seq[String],
42 | children: (js.Object, js.Object) => ReactElement)
43 |
44 | def apply[Res](query: DocumentNode)
45 | (children: (CallMutationProps[Unit] => Future[MutationResult[Res]], MutationData[Res]) => ReactElement)
46 | (implicit tReader: Reader[Res]): slinky.core.BuildingComponent[Element, js.Object] = {
47 | apply(query, UpdateStrategy())(children)
48 | }
49 |
50 | type DynamicMutationAsyncCallback[Var, Res] = CallMutationProps[Var] => Future[MutationResult[Res]]
51 |
52 | def apply[Res, Var](query: DocumentNode)
53 | (children: (DynamicMutationAsyncCallback[Var, Res], MutationData[Res]) => ReactElement)
54 | (implicit tReader: Reader[Res], vWriter: Writer[Var]): slinky.core.BuildingComponent[Element, js.Object] = {
55 | apply(query, UpdateStrategy())(children)
56 | }
57 |
58 | def apply[Res, Var](query: DocumentNode, updateStrategy: UpdateStrategy)
59 | (children: (DynamicMutationAsyncCallback[Var, Res], MutationData[Res]) => ReactElement)
60 | (implicit tReader: Reader[Res], vWriter: Writer[Var]): slinky.core.BuildingComponent[Element, js.Object] = {
61 | val queryDataReader = MutationData.reader(tReader)
62 | apply(Props(
63 | mutation = query,
64 | refetchQueries = updateStrategy.refetchQueries,
65 | children = (call, d) => {
66 | children(
67 | implicitly[Reader[CallMutationProps[Var] => Future[MutationResult[Res]]]].read(call),
68 | queryDataReader.read(d)
69 | )
70 | }
71 | ))
72 | }
73 |
74 | type StaticMutationAsyncCallback[Q <: GraphQLMutation] = CallMutationProps[Q#Variables] => Future[MutationResult[Q#Data]]
75 |
76 | def apply[Q <: GraphQLMutation](query: Q)
77 | (children: (StaticMutationAsyncCallback[Q], MutationData[Q#Data]) => ReactElement)
78 | (implicit dataReader: Reader[Q#Data],
79 | variablesWriter: Writer[Q#Variables]): slinky.core.BuildingComponent[Element, js.Object] = {
80 | apply(query: Q, UpdateStrategy())(children)
81 | }
82 |
83 | def apply[Q <: GraphQLMutation](query: Q, updateStrategy: UpdateStrategy)
84 | (children: (StaticMutationAsyncCallback[Q], MutationData[Q#Data]) => ReactElement)
85 | (implicit dataReader: Reader[Q#Data],
86 | variablesWriter: Writer[Q#Variables]): slinky.core.BuildingComponent[Element, js.Object] = {
87 | val queryDataReader = MutationData.reader(dataReader)
88 | apply(Props(
89 | mutation = query.operation,
90 | refetchQueries = updateStrategy.refetchQueries,
91 | children = (call, d) => {
92 | children(
93 | implicitly[Reader[CallMutationProps[Q#Variables] => Future[MutationResult[Q#Data]]]].read(call),
94 | queryDataReader.read(d)
95 | )
96 | }
97 | ))
98 | }
99 |
100 | @js.native
101 | @JSImport("@apollo/client/react/components", "Mutation")
102 | object MutationComponent extends js.Object
103 |
104 | override val component = MutationComponent
105 | }
106 |
--------------------------------------------------------------------------------
/react/src/main/scala/com/apollographql/scalajs/react/Hooks.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import scala.scalajs.js
4 | import scala.scalajs.js.annotation.JSImport
5 | import com.apollographql.scalajs._
6 |
7 | @JSImport("@apollo/client", JSImport.Namespace)
8 | @js.native
9 | object HooksApi extends js.Object {
10 | def useQuery[Q <: GraphQLQuery](
11 | query: DocumentNode,
12 | props: js.UndefOr[QueryHookOptions[Q]] = js.undefined
13 | ): QueryResult[Q] = js.native
14 |
15 | def useMutation[Q <: GraphQLMutation](
16 | query: DocumentNode,
17 | props: js.UndefOr[MutationHookOptions[Q]] = js.undefined
18 | ): js.Tuple2[js.Function1[js.UndefOr[MutationHookOptions[Q]], js.Promise[MutationResult[Q#Data]]], MutationResult[Q#Data]] = js.native
19 | }
20 |
21 | trait QueryHookOptions[Q <: GraphQLQuery] extends js.Object {
22 | val variables: js.UndefOr[Q#Variables] = js.undefined
23 | val pollInterval: js.UndefOr[Int] = js.undefined
24 | val notifyOnNetworkStatusChange: js.UndefOr[Boolean] = js.undefined
25 | val fetchPolicy: js.UndefOr[FetchPolicy] = js.undefined
26 | val errorPolicy: js.UndefOr[ErrorPolicy] = js.undefined
27 | val ssr: js.UndefOr[Boolean] = js.undefined
28 | val displayName: js.UndefOr[String] = js.undefined
29 | val skip: js.UndefOr[Boolean] = js.undefined
30 | val partialRefetch: js.UndefOr[Boolean] = js.undefined
31 | val client: js.UndefOr[ApolloClient] = js.undefined
32 | val returnPartialData: js.UndefOr[Boolean] = js.undefined
33 | }
34 |
35 | object QueryHookOptions {
36 | def apply[Q <: GraphQLQuery](
37 | variables: js.UndefOr[Q#Variables] = js.undefined,
38 | pollInterval: js.UndefOr[Int] = js.undefined,
39 | notifyOnNetworkStatusChange: js.UndefOr[Boolean] = js.undefined,
40 | fetchPolicy: js.UndefOr[FetchPolicy] = js.undefined,
41 | errorPolicy: js.UndefOr[ErrorPolicy] = js.undefined,
42 | ssr: js.UndefOr[Boolean] = js.undefined,
43 | displayName: js.UndefOr[String] = js.undefined,
44 | skip: js.UndefOr[Boolean] = js.undefined,
45 | partialRefetch: js.UndefOr[Boolean] = js.undefined,
46 | client: js.UndefOr[ApolloClient] = js.undefined,
47 | returnPartialData: js.UndefOr[Boolean] = js.undefined
48 | ): QueryHookOptions[Q] = {
49 | js.Dynamic
50 | .literal(
51 | variables = variables.asInstanceOf[js.Object],
52 | pollInterval = pollInterval,
53 | notifyOnNetworkStatusChange = notifyOnNetworkStatusChange,
54 | fetchPolicy = fetchPolicy,
55 | errorPolicy = errorPolicy,
56 | ssr = ssr,
57 | displayName = displayName,
58 | skip = skip,
59 | partialRefetch = partialRefetch,
60 | client = client,
61 | returnPartialData = returnPartialData
62 | )
63 | .asInstanceOf[QueryHookOptions[Q]]
64 | }
65 | }
66 |
67 | trait FetchPolicy extends js.Object
68 |
69 | object FetchPolicy {
70 | val CacheFirst: FetchPolicy = "cache-first".asInstanceOf[FetchPolicy]
71 | val NetworkOnly: FetchPolicy = "network-only".asInstanceOf[FetchPolicy]
72 | val CacheOnly: FetchPolicy = "cache-only".asInstanceOf[FetchPolicy]
73 | val NoCache: FetchPolicy = "no-cache".asInstanceOf[FetchPolicy]
74 | val Standby: FetchPolicy = "standby".asInstanceOf[FetchPolicy]
75 | val CacheAndNetwork: FetchPolicy = "cache-and-network".asInstanceOf[FetchPolicy]
76 | }
77 |
78 |
79 | trait ErrorPolicy extends js.Object
80 |
81 | object ErrorPolicy {
82 | val None: ErrorPolicy = "none".asInstanceOf[ErrorPolicy]
83 | val Ignore: ErrorPolicy = "ignore".asInstanceOf[ErrorPolicy]
84 | val All: ErrorPolicy = "all".asInstanceOf[ErrorPolicy]
85 | }
86 |
87 | @js.native
88 | trait QueryResult[Q <: GraphQLQuery] extends js.Object {
89 | val data: js.UndefOr[Q#Data] = js.native
90 | val loading: Boolean = js.native
91 | val error: js.UndefOr[js.Error] = js.native
92 | val variables: js.UndefOr[Q#Variables] = js.native
93 | val networkStatus: Int = js.native
94 | val refetch: js.Function1[Q#Variables, js.Promise[ApolloQueryResult[Q#Data]]] = js.native
95 | val fetchMore: js.Function3[DocumentNode, Q#Variables, js.Function, js.Promise[ApolloQueryResult[Q#Data]]] = js.native
96 | val startPolling: js.Function1[Int, Unit] = js.native
97 | val subscribeToMore: js.Function1[js.Dynamic, js.Function0[Unit]] = js.native
98 | val updateQuery: js.Function2[Q#Data, js.Dynamic, Q#Data] = js.native
99 | val client: ApolloClient = js.native
100 | val called: Boolean = js.native
101 | }
102 |
103 | object QueryResult {
104 | // An unapply for the common use case to extract values
105 | def unapply[Q <: GraphQLQuery](result: QueryResult[Q]): Option[(js.UndefOr[Q#Data], js.UndefOr[js.Error], Boolean)] = {
106 | Some((result.data, result.error, result.loading))
107 | }
108 | }
109 |
110 | @js.native
111 | trait MutationHookOptions[Q <: GraphQLMutation] extends js.Object {
112 | val variables: js.UndefOr[Q#Variables] = js.undefined
113 | val refetchQueries: js.UndefOr[js.Array[js.Dynamic]] = js.undefined
114 | }
115 |
116 | object MutationHookOptions {
117 | def apply[Q <: GraphQLMutation](
118 | variables: js.UndefOr[Q#Variables] = js.undefined,
119 | refetchQueries: js.UndefOr[js.Array[js.Dynamic]] = js.undefined
120 | ): MutationHookOptions[Q] = {
121 | js.Dynamic
122 | .literal(
123 | variables = variables.asInstanceOf[js.Object],
124 | refetchQueries = refetchQueries
125 | )
126 | .asInstanceOf[MutationHookOptions[Q]]
127 | }
128 | }
129 |
130 | @js.native
131 | trait MutationResult[D] extends js.Object {
132 | val data: js.UndefOr[D] = js.native
133 | val loading: Boolean = js.native
134 | val error: js.UndefOr[js.Error] = js.native
135 | val called: Boolean = js.native
136 | val client: ApolloClient = js.native
137 | }
138 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/react/QueryComponentTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs._
4 | import org.scalajs.dom.document
5 | import org.scalatest.funsuite.AsyncFunSuite
6 | import org.scalatest.Assertion
7 | import slinky.web.ReactDOM
8 | import slinky.web.html.div
9 |
10 | import scala.concurrent.Promise
11 | import scala.scalajs.js
12 | import com.apollographql.scalajs.cache.InMemoryCache
13 |
14 | class QueryComponentTest extends AsyncFunSuite {
15 | js.Dynamic.global.window.fetch = UnfetchFetch
16 |
17 | implicit override def executionContext =
18 | scala.concurrent.ExecutionContext.Implicits.global
19 |
20 | test("Can mount a Query component and render data based on the query") {
21 | val gotDataPromise = Promise[Assertion]
22 | case class ResultShape(rates: Seq[js.Object])
23 | ReactDOM.render(
24 | ApolloProvider(
25 | client = new ApolloClient(
26 | ApolloClientOptions(
27 | uri = "https://graphql-currency-rates.glitch.me",
28 | cache = new InMemoryCache()
29 | )
30 | )
31 | )(
32 | Query[ResultShape](gql(
33 | """{
34 | | rates(currency: "USD") {
35 | | currency
36 | | }
37 | |}""".stripMargin
38 | )) { d =>
39 | if (d.data.isDefined) {
40 | gotDataPromise.success(assert(d.data.get.rates.nonEmpty))
41 | }
42 |
43 | div()
44 | }
45 | ),
46 | document.createElement("div")
47 | )
48 |
49 | gotDataPromise.future
50 | }
51 |
52 | test("Can mount a Query component that takes variables and render data based on the query") {
53 | val gotDataPromise = Promise[Assertion]
54 | case class ResultShape(rates: Seq[js.Object])
55 | case class Variables(cur: String)
56 | ReactDOM.render(
57 | ApolloProvider(
58 | client = new ApolloClient(
59 | ApolloClientOptions(
60 | uri = "https://graphql-currency-rates.glitch.me",
61 | cache = new InMemoryCache()
62 | )
63 | )
64 | )(
65 | Query[ResultShape, Variables](gql(
66 | """query GetRates($cur: String!) {
67 | | rates(currency: $cur) {
68 | | currency
69 | | }
70 | |}""".stripMargin
71 | ), Variables("USD")) { d =>
72 | if (d.data.isDefined) {
73 | gotDataPromise.success(assert(d.data.get.rates.nonEmpty))
74 | }
75 |
76 | div()
77 | }
78 | ),
79 | document.createElement("div")
80 | )
81 |
82 | gotDataPromise.future
83 | }
84 |
85 | test("Can mount a Query component and render data based a query returning multiple values") {
86 | val gotDataPromise = Promise[Assertion]
87 | case class ResultShape(rates: Seq[js.Object], bar: Seq[js.Object])
88 | ReactDOM.render(
89 | ApolloProvider(
90 | client = new ApolloClient(
91 | ApolloClientOptions(
92 | uri = "https://graphql-currency-rates.glitch.me",
93 | cache = new InMemoryCache()
94 | )
95 | )
96 | )(
97 | Query[ResultShape](gql(
98 | """{
99 | | rates(currency: "USD") {
100 | | currency
101 | | }
102 | |
103 | | bar: rates(currency: "USD") {
104 | | currency
105 | | }
106 | |}""".stripMargin
107 | )) { d =>
108 | if (d.data.isDefined) {
109 | gotDataPromise.success(assert(d.data.get.rates.nonEmpty && d.data.get.bar.nonEmpty))
110 | }
111 |
112 | div()
113 | }
114 | ),
115 | document.createElement("div")
116 | )
117 |
118 | gotDataPromise.future
119 | }
120 |
121 | test("Can mount a Query component and render data based a static query") {
122 | val gotDataPromise = Promise[Assertion]
123 | ReactDOM.render(
124 | ApolloProvider(
125 | client = new ApolloClient(
126 | ApolloClientOptions(
127 | uri = "https://graphql-currency-rates.glitch.me",
128 | cache = new InMemoryCache()
129 | )
130 | )
131 | )(
132 | Query(UsdRatesQuery) { d =>
133 | if (d.data.isDefined) {
134 | gotDataPromise.success(assert(d.data.get.rates.exists(_.nonEmpty)))
135 | }
136 |
137 | div()
138 | }
139 | ),
140 | document.createElement("div")
141 | )
142 |
143 | gotDataPromise.future
144 | }
145 |
146 | test("Can mount a Query component and render data based a static query with variables") {
147 | val gotDataPromise = Promise[Assertion]
148 | ReactDOM.render(
149 | ApolloProvider(
150 | client = new ApolloClient(
151 | ApolloClientOptions(
152 | uri = "https://graphql-currency-rates.glitch.me",
153 | cache = new InMemoryCache()
154 | )
155 | )
156 | )(
157 | Query(CurrencyRatesQuery, CurrencyRatesQuery.Variables("USD")) { d =>
158 | if (d.data.isDefined) {
159 | gotDataPromise.success(assert(d.data.get.rates.exists(_.nonEmpty)))
160 | }
161 |
162 | div()
163 | }
164 | ),
165 | document.createElement("div")
166 | )
167 |
168 | gotDataPromise.future
169 | }
170 |
171 | test("Can mount a Query component with a cache-only fetch policy") {
172 | val didntLoadPromise = Promise[Assertion]
173 | ReactDOM.render(
174 | ApolloProvider(
175 | client = new ApolloClient(
176 | ApolloClientOptions(
177 | uri = "https://this-does-not-exists.com",
178 | cache = new InMemoryCache()
179 | )
180 | )
181 | )(
182 | Query(CurrencyRatesQuery, CurrencyRatesQuery.Variables("USD"), ExtraQueryOptions(
183 | fetchPolicy = "cache-only"
184 | )) { d =>
185 | // TODO: Check if this is the intended behavior
186 | if (!d.loading && d.error.isEmpty) {
187 | didntLoadPromise.trySuccess(assert(true))
188 | }
189 |
190 | div()
191 | }
192 | ),
193 | document.createElement("div")
194 | )
195 |
196 | didntLoadPromise.future
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/react/src/main/scala/com/apollographql/scalajs/react/Query.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs.{GraphQLQuery, DocumentNode}
4 | import slinky.core.ExternalComponent
5 | import slinky.core.facade.ReactElement
6 | import slinky.readwrite.{Reader, Writer}
7 |
8 | import scala.util.Try
9 | import scala.scalajs.js
10 | import scala.scalajs.js.|
11 | import scala.scalajs.js.annotation.JSImport
12 |
13 | case class QueryData[T](loading: Boolean, error: Option[js.Error], data: Option[T], refetch: () => Unit)
14 | object QueryData {
15 | implicit def reader[T](implicit tReader: Reader[T]): Reader[QueryData[T]] = { o =>
16 | val dyn = o.asInstanceOf[js.Dynamic]
17 | val loading = Reader.booleanReader.read(dyn.loading.asInstanceOf[js.Object])
18 | val error = Reader.optionReader[js.Error].read(dyn.error.asInstanceOf[js.Object])
19 | QueryData(
20 | loading,
21 | error,
22 | if (!js.isUndefined(dyn.data) && js.Object.keys(dyn.data.asInstanceOf[js.Object]).nonEmpty) {
23 | Some(tReader.read(dyn.data.asInstanceOf[js.Object]))
24 | } else None,
25 | implicitly[Reader[() => Unit]].read(dyn.refetch.asInstanceOf[js.Object])
26 | )
27 | }
28 | }
29 |
30 | case class ExtraQueryOptions(pollInterval: js.UndefOr[Double] = js.undefined,
31 | notifyOnNetworkStatusChange: js.UndefOr[Boolean] = js.undefined,
32 | fetchPolicy: js.UndefOr[String] = js.undefined,
33 | errorPolicy: js.UndefOr[String] = js.undefined,
34 | ssr: js.UndefOr[Boolean] = js.undefined,
35 | displayName: js.UndefOr[String] = js.undefined,
36 | delay: js.UndefOr[Boolean] = js.undefined,
37 | context: js.UndefOr[js.Object] = js.undefined)
38 |
39 | object Query extends ExternalComponent {
40 | case class Props(query: DocumentNode,
41 | children: js.Object => ReactElement,
42 | variables: js.UndefOr[js.Object] = js.undefined,
43 | pollInterval: js.UndefOr[Double] = js.undefined,
44 | notifyOnNetworkStatusChange: js.UndefOr[Boolean] = js.undefined,
45 | fetchPolicy: js.UndefOr[String] = js.undefined,
46 | errorPolicy: js.UndefOr[String] = js.undefined,
47 | ssr: js.UndefOr[Boolean] = js.undefined,
48 | displayName: js.UndefOr[String] = js.undefined,
49 | delay: js.UndefOr[Boolean] = js.undefined,
50 | context: js.UndefOr[js.Object] = js.undefined)
51 | def apply[T, V](query: DocumentNode, variables: V, queryOptions: ExtraQueryOptions)
52 | (children: QueryData[T] => ReactElement)
53 | (implicit tReader: Reader[T], vWriter: Writer[V]): slinky.core.BuildingComponent[Element, js.Object] = {
54 | val queryDataReader = QueryData.reader(tReader)
55 | apply(Props(
56 | query = query,
57 | variables = vWriter.write(variables),
58 | children = d => {
59 | children(queryDataReader.read(d))
60 | },
61 | // queryOptions
62 | pollInterval = queryOptions.pollInterval,
63 | notifyOnNetworkStatusChange = queryOptions.notifyOnNetworkStatusChange,
64 | fetchPolicy = queryOptions.fetchPolicy,
65 | errorPolicy = queryOptions.errorPolicy,
66 | ssr = queryOptions.ssr,
67 | displayName = queryOptions.displayName,
68 | delay = queryOptions.delay,
69 | context = queryOptions.context
70 | ))
71 | }
72 |
73 | def apply[T, V](query: DocumentNode, variables: V)
74 | (children: QueryData[T] => ReactElement)
75 | (implicit tReader: Reader[T], vWriter: Writer[V]): slinky.core.BuildingComponent[Element, js.Object] = {
76 | apply[T, V](
77 | query = query,
78 | variables = variables,
79 | queryOptions = ExtraQueryOptions()
80 | )(children)
81 | }
82 |
83 | def apply[T](query: DocumentNode, queryOptions: ExtraQueryOptions)
84 | (children: QueryData[T] => ReactElement)
85 | (implicit tReader: Reader[T]): slinky.core.BuildingComponent[Element, js.Object] = {
86 | apply[T, Unit](
87 | query = query,
88 | (),
89 | queryOptions
90 | )(children)
91 | }
92 |
93 | def apply[T](query: DocumentNode)
94 | (children: QueryData[T] => ReactElement)
95 | (implicit tReader: Reader[T]): slinky.core.BuildingComponent[Element, js.Object] = {
96 | apply[T](
97 | query = query,
98 | queryOptions = ExtraQueryOptions()
99 | )(children)
100 | }
101 |
102 | def apply[Q <: GraphQLQuery](query: Q, variables: Q#Variables, queryOptions: ExtraQueryOptions)
103 | (children: QueryData[query.Data] => ReactElement)
104 | (implicit dataReader: Reader[query.Data],
105 | variablesWriter: Writer[Q#Variables]): slinky.core.BuildingComponent[Element, js.Object] = {
106 | apply[query.Data, Q#Variables](
107 | query = query.operation,
108 | variables = variables,
109 | queryOptions = queryOptions
110 | )(children)
111 | }
112 |
113 | def apply[Q <: GraphQLQuery](query: Q, variables: Q#Variables)
114 | (children: QueryData[query.Data] => ReactElement)
115 | (implicit dataReader: Reader[query.Data],
116 | variablesWriter: Writer[Q#Variables]): slinky.core.BuildingComponent[Element, js.Object] = {
117 | apply[Q](
118 | query = query,
119 | variables = variables,
120 | queryOptions = ExtraQueryOptions()
121 | )(children)
122 | }
123 |
124 | def apply(query: GraphQLQuery { type Variables = Unit }, queryOptions: ExtraQueryOptions)
125 | (children: QueryData[query.Data] => ReactElement)
126 | (implicit dataReader: Reader[query.Data]): slinky.core.BuildingComponent[Element, js.Object] = {
127 | apply[GraphQLQuery {type Variables = Unit }](
128 | query = query,
129 | variables = (),
130 | queryOptions = queryOptions
131 | )(children)
132 | }
133 |
134 | def apply(query: GraphQLQuery { type Variables = Unit })
135 | (children: QueryData[query.Data] => ReactElement)
136 | (implicit dataReader: Reader[query.Data]): slinky.core.BuildingComponent[Element, js.Object] = {
137 | apply(
138 | query = query,
139 | queryOptions = ExtraQueryOptions()
140 | )(children)
141 | }
142 |
143 | @js.native
144 | @JSImport("@apollo/client/react/components", "Query")
145 | object QueryComponent extends js.Object
146 |
147 | override val component = QueryComponent
148 | }
149 |
--------------------------------------------------------------------------------
/docs/source/essentials/queries.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Queries
3 | order: 2
4 | ---
5 |
6 | Now that you have your client set up, let's dive in deeper and see how to use Query components to load data into your application.
7 |
8 | ## The Query component
9 | Using the Query component from Scala.js is just like using it with the original JavaScript API. Simply pass in a parsed GraphQL query object (from `gql`) and a function that decides what to render given the state of the query. Because the Query component tracks all steps of making a query, you can easily render loading and error states by reading properties of the query status given to you.
10 |
11 | When you use the Query component, Apollo Client also performs caching to minimize server roundtrips. If the data requested is already in the cache, your Query component will immediately render with the cached data. If it isn't, the result of the query will be normalized and placed into the cache as soon as the data arrives.
12 |
13 | Let's see this in action!
14 |
15 | ```scala
16 | import com.apollographql.scalajs.react.Query
17 |
18 | Query[js.Object](gql(
19 | """{
20 | | rates(currency: "USD") {
21 | | currency
22 | | }
23 | |}""".stripMargin
24 | )) { result =>
25 | if (result.loading) {
26 | h1("Loading!")
27 | } else {
28 | div(result.data.get.toString)
29 | }
30 | }
31 | ```
32 |
33 | Here, we specified the type of the result to be `js.Object`, essentially making it untyped. This data isn't super friendly to work with, though, so let's see how we can type our data.
34 |
35 | ## Typing Query Responses
36 | Apollo Scala.js allows you to specify models to parse the query results into. These models are usually case classes and can be handwritten or generated by Apollo CLI (see [automatic query types](#automatic-query-types) for how to set this up).
37 |
38 | Let's say we have a query
39 | ```graphql
40 | {
41 | dogs {
42 | id
43 | breed
44 | }
45 | }
46 | ```
47 |
48 | The corresponding type for this would look something like this:
49 | ```scala
50 | case class Dog(id: String, breed: String)
51 | case class QueryData(dogs: Seq[Dog])
52 | ```
53 |
54 | Once we have this type written, all we have to do is replace `js.Object` with `QueryData` to get typed responses! Apollo Scala.js handles the process of parsing the result into Scala objects, so you can focus on using the data.
55 |
56 | ```scala
57 | Query[QueryData](gql(
58 | """{
59 | | dogs {
60 | | id
61 | | breed
62 | | }
63 | |}""".stripMargin
64 | )) { queryStatus =>
65 | if (queryStatus.loading) "Loading..."
66 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
67 | else {
68 | div(
69 | queryStatus.data.get.dogs.map { dog =>
70 | h1(key := dog.id)(dog.breed)
71 | }
72 | )
73 | }
74 | }
75 | ```
76 |
77 | ## Querying with Variables
78 | When performing a GraphQL query with a Query component, you can also pass in variables to be used by the query as an additional parameter. Just like the result, variables can be typed with an additional type parameter, or left untyped by using `js.Object` as the type.
79 |
80 | To perform a query with untyped variables, you can use `js.Dynamic.literal` to construct the variables object.
81 | ```scala
82 | case class Dog(id: String, breed: String)
83 | case class QueryData(dog: Dog)
84 |
85 | Query[QueryData, js.Object](
86 | gql(
87 | """query dog($breed: String!) {
88 | | dog(breed: $breed) {
89 | | id
90 | | displayImage
91 | | }
92 | |}""".stripMargin
93 | ),
94 | js.Dynamic.literal(
95 | breed = "bulldog"
96 | )
97 | ) { queryStatus =>
98 | if (queryStatus.loading) "Loading..."
99 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
100 | else {
101 | img(src := queryStatus.data.get.dog.displayImage)
102 | }
103 | }
104 | ```
105 |
106 | If we want to type the variables being passed into the query, we can define a case class to replace `js.Object` in the type parameters.
107 | ```scala
108 | case class Variables(breed: String)
109 |
110 | Query[QueryData, Variables](
111 | gql(
112 | """query dog($breed: String!) {
113 | | dog(breed: $breed) {
114 | | id
115 | | displayImage
116 | | }
117 | |}""".stripMargin
118 | ),
119 | Variables(breed = "bulldog")
120 | ) { queryStatus =>
121 | if (queryStatus.loading) "Loading..."
122 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
123 | else {
124 | img(src := queryStatus.data.get.dog.displayImage)
125 | }
126 | }
127 | ```
128 |
129 | ## Automatic Query Types
130 |
131 | With `apollo`, we can automatically generate query objects that tie together GraphQL queries with response types based on the schema definition. First, make sure you have followed the [Apollo CLI Installation](/essentials/installation/#apollo-cli) steps and downloaded a schema by following the [instructions](https://github.com/apollographql/apollo-cli#apollo-schemadownload-output). For this example, we will be using the GraphQL server at `https://graphql-currency-rates.glitch.me`.
132 |
133 | With Apollo CLI installed, we can define our first query! Under `src/main/graphql`, you can define static queries that will be converted to Scala code by Apollo CLI. For example, we could define a query `usdrates.graphql`:
134 |
135 | ```graphql
136 | query USDRates {
137 | rates(currency: "USD") {
138 | currency
139 | }
140 | }
141 | ```
142 |
143 | Which results in an object `UsdRatesQuery` being generated. To use this in a query component, we can simply pass the generated object in instead of a query string and Apollo Scala.js will automatically gather the result type based on the generated code.
144 |
145 | ```scala
146 | Query(UsdRatesQuery) { queryStatus =>
147 | if (queryStatus.loading) "Loading..."
148 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
149 | else {
150 | div(queryStatus.data.get.rates.mkString(", "))
151 | }
152 | }
153 | ```
154 |
155 | Apollo CLI also supports generating variables types, which are emitted as case classes inside the query object. To pass in variables to a query that requires some, simply pass in an instance of the variables class as a parameter after the query object.
156 |
157 | For a query:
158 |
159 | ```graphql
160 | query CurrencyRates($cur: String!) {
161 | rates(currency: $cur) {
162 | currency
163 | }
164 | }
165 | ```
166 |
167 | We can pass in variables in a query component as:
168 |
169 | ```scala
170 | Query(CurrencyRatesQuery, CurrencyRatesQuery.Variables("USD")) { queryStatus =>
171 | if (queryStatus.loading) "Loading..."
172 | else if (queryStatus.error) s"Error! ${queryStatus.error.message}"
173 | else {
174 | div(queryStatus.data.get.rates.mkString(", "))
175 | }
176 | }
177 | ```
178 |
179 | ## Extra Query Options
180 | If you want to pass in additional query options, such as the fetch policy, you can provide an instance of `ExtraQueryOptions` as an additional parameter. For example, to force the query to only load from the cache you can use:
181 |
182 | ```scala
183 | Query(CurrencyRatesQuery, CurrencyRatesQuery.Variables("USD"), ExtraQueryOptions(
184 | fetchPolicy = "cache-only"
185 | )) { ... }
186 | ```
187 |
188 | ## Next steps
189 | Now that you've learned how to get data from your GraphQL server, it's time to learn to update that data with [Mutations](/essentials/mutations/)!
190 |
--------------------------------------------------------------------------------
/tests/src/test/scala/com/apollographql/scalajs/react/MutationComponentTest.scala:
--------------------------------------------------------------------------------
1 | package com.apollographql.scalajs.react
2 |
3 | import com.apollographql.scalajs.AddTodoMutation
4 | import com.apollographql.scalajs.AllTodosIdQuery
5 | import com.apollographql.scalajs.UnfetchFetch
6 | import com.apollographql.scalajs.gql
7 | import org.scalajs.dom.document
8 | import org.scalatest.Assertion
9 | import org.scalatest.funsuite.AsyncFunSuite
10 | import org.scalatest.matchers.should.Matchers
11 | import slinky.web.ReactDOM
12 | import slinky.web.html.div
13 |
14 | import scala.concurrent.Future
15 | import scala.concurrent.Promise
16 | import scala.scalajs.js
17 | import scala.util.Failure
18 | import scala.util.Success
19 | import com.apollographql.scalajs.ApolloClient
20 | import com.apollographql.scalajs.ApolloClientOptions
21 | import com.apollographql.scalajs.cache.InMemoryCache
22 |
23 | class MutationComponentTest extends AsyncFunSuite with Matchers {
24 | js.Dynamic.global.window.fetch = UnfetchFetch
25 |
26 | implicit override def executionContext =
27 | scala.concurrent.ExecutionContext.Implicits.global
28 |
29 | trait Todo extends js.Object {
30 | val typ: String
31 | }
32 |
33 | trait TodoResult extends js.Object {
34 | val addTodo: Todo
35 | }
36 |
37 | test("Can mount a Mutation component and call the mutation") {
38 | val gotDataPromise = Promise[Assertion]
39 |
40 | var callMutation: () => Unit = null
41 |
42 | ReactDOM.render(
43 | ApolloProvider(
44 | client = new ApolloClient(
45 | ApolloClientOptions(
46 | uri = "https://graphql-todo-tracker.glitch.me",
47 | cache = new InMemoryCache()
48 | )
49 | )
50 | )(
51 | Mutation[TodoResult, Unit](gql(
52 | """mutation {
53 | | addTodo(type: "lol") {
54 | | typ: type
55 | | }
56 | |}""".stripMargin
57 | )) { (mut, d) =>
58 | if (d.data.isDefined) {
59 | gotDataPromise.success(assert(d.data.get.addTodo.typ == "lol"))
60 | }
61 |
62 | callMutation = () => {
63 | mut(())
64 | }
65 |
66 | div()
67 | }
68 | ),
69 | document.createElement("div")
70 | )
71 |
72 | callMutation()
73 |
74 | gotDataPromise.future
75 | }
76 |
77 | test("Can mount a Mutation component and get an error when the mutation fails") {
78 | val gotFailurePromise = Promise[Assertion]
79 |
80 | var ranMutation = false
81 |
82 | ReactDOM.render(
83 | ApolloProvider(
84 | client = new ApolloClient(
85 | ApolloClientOptions(
86 | uri = "https://graphql-todo-tracker.glitch.me",
87 | cache = new InMemoryCache()
88 | )
89 | )
90 | )(
91 | Mutation[Unit, Unit](gql(
92 | """mutation {
93 | | randomMutationThatDoesntExist(type: "lol") {
94 | | typ: type
95 | | }
96 | |}""".stripMargin
97 | )) { (mut, d) =>
98 | if (!ranMutation) {
99 | ranMutation = true
100 | mut(()).andThen {
101 | case Success(_) =>
102 | gotFailurePromise.failure(new Exception("Succeeded when it shouldn't have"))
103 | case Failure(_) =>
104 | gotFailurePromise.success(assert(true))
105 | }
106 | }
107 |
108 | div()
109 | }
110 | ),
111 | document.createElement("div")
112 | )
113 |
114 | gotFailurePromise.future
115 | }
116 |
117 | test("Can mount a Mutation component and call the mutation with variables") {
118 | val gotDataPromise = Promise[Assertion]
119 |
120 | var callMutation: () => Unit = null
121 |
122 | ReactDOM.render(
123 | ApolloProvider(
124 | client = new ApolloClient(
125 | ApolloClientOptions(
126 | uri = "https://graphql-todo-tracker.glitch.me",
127 | cache = new InMemoryCache()
128 | )
129 | )
130 | )(
131 | Mutation[TodoResult, js.Object](gql(
132 | """mutation AddTodo($typ: String!) {
133 | | addTodo(type: $typ) {
134 | | typ: type
135 | | }
136 | |}""".stripMargin
137 | )) { (mut, d) =>
138 | if (d.data.isDefined) {
139 | gotDataPromise.success(assert(d.data.get.addTodo.typ == "bar"))
140 | }
141 |
142 | callMutation = () => {
143 | mut(js.Dynamic.literal(typ = "bar"))
144 | }
145 |
146 | div()
147 | }
148 | ),
149 | document.createElement("div")
150 | )
151 |
152 | callMutation()
153 |
154 | gotDataPromise.future
155 | }
156 |
157 | test("Can mount a Mutation component, call the mutation, and get the result from the future") {
158 | var resultFuture: Future[Assertion] = null
159 |
160 | ReactDOM.render(
161 | ApolloProvider(
162 | client = new ApolloClient(
163 | ApolloClientOptions(
164 | uri = "https://graphql-todo-tracker.glitch.me",
165 | cache = new InMemoryCache()
166 | )
167 | )
168 | )(
169 | Mutation[TodoResult, js.Object](gql(
170 | """mutation AddTodo($typ: String!) {
171 | | addTodo(type: $typ) {
172 | | typ: type
173 | | }
174 | |}""".stripMargin
175 | )) { (mut, _) =>
176 | if (resultFuture == null) {
177 | resultFuture = mut(js.Dynamic.literal(typ = "bar")).map { d =>
178 | assert(d.data.get.addTodo.typ == "bar")
179 | }
180 | }
181 |
182 | div()
183 | }
184 | ),
185 | document.createElement("div")
186 | )
187 |
188 | resultFuture
189 | }
190 |
191 | test("Can mount a Mutation component with a static mutation and call the mutation with variables") {
192 | val gotDataPromise = Promise[Assertion]
193 |
194 | var callMutation: () => Unit = null
195 |
196 | ReactDOM.render(
197 | ApolloProvider(
198 | client = new ApolloClient(
199 | ApolloClientOptions(
200 | uri = "https://graphql-todo-tracker.glitch.me",
201 | cache = new InMemoryCache()
202 | )
203 | )
204 | )(
205 | Mutation(AddTodoMutation) { (mut, res) =>
206 | if (res.data.isDefined) {
207 | gotDataPromise.success(assert(res.data.get.addTodo.get.typ == "bar"))
208 | }
209 |
210 | callMutation = () => {
211 | mut(AddTodoMutation.Variables(typ = "bar"))
212 | }
213 |
214 | div()
215 | }
216 | ),
217 | document.createElement("div")
218 | )
219 |
220 | callMutation()
221 |
222 | gotDataPromise.future
223 | }
224 |
225 | test("Can mount a Mutation component with refetchQueries and refreshes the query") {
226 | val multipleCallPromise = Promise[Assertion]
227 |
228 | var callMutation: () => Unit = null
229 |
230 | var todoSizeAccumulator: Seq[Int] = Seq()
231 |
232 | ReactDOM.render(
233 | ApolloProvider(
234 | client = new ApolloClient(
235 | ApolloClientOptions(
236 | uri = "https://graphql-todo-tracker.glitch.me",
237 | cache = new InMemoryCache()
238 | )
239 | )
240 | )(
241 | div(
242 | Query(AllTodosIdQuery) { result =>
243 |
244 | if(result.data.isDefined) {
245 | val todos = result.data.get.todos.get.size
246 | todoSizeAccumulator = todoSizeAccumulator :+ todos
247 |
248 | // If the query has been called twice (i.e. refetch happened)
249 | if (todoSizeAccumulator.size == 2) {
250 | multipleCallPromise.success(todoSizeAccumulator.reverse.reduce(_ - _) shouldBe 1)
251 | }
252 | }
253 |
254 | div()
255 | },
256 | Mutation(AddTodoMutation, UpdateStrategy(refetchQueries = Seq("AllTodosId"))) { (mut, res) =>
257 | callMutation = () => {
258 | mut(AddTodoMutation.Variables(typ = "refresh"))
259 | }
260 | div()
261 | }
262 | )
263 | ),
264 | document.createElement("div")
265 | )
266 |
267 | callMutation()
268 |
269 | multipleCallPromise.future
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/tests/queries.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "__schema": {
4 | "queryType": {
5 | "name": "Query"
6 | },
7 | "mutationType": null,
8 | "subscriptionType": null,
9 | "types": [
10 | {
11 | "kind": "OBJECT",
12 | "name": "Query",
13 | "description": "",
14 | "fields": [
15 | {
16 | "name": "rates",
17 | "description": "",
18 | "args": [
19 | {
20 | "name": "currency",
21 | "description": "",
22 | "type": {
23 | "kind": "NON_NULL",
24 | "name": null,
25 | "ofType": {
26 | "kind": "SCALAR",
27 | "name": "String",
28 | "ofType": null
29 | }
30 | },
31 | "defaultValue": null
32 | }
33 | ],
34 | "type": {
35 | "kind": "LIST",
36 | "name": null,
37 | "ofType": {
38 | "kind": "OBJECT",
39 | "name": "ExchangeRate",
40 | "ofType": null
41 | }
42 | },
43 | "isDeprecated": false,
44 | "deprecationReason": null
45 | }
46 | ],
47 | "inputFields": null,
48 | "interfaces": [],
49 | "enumValues": null,
50 | "possibleTypes": null
51 | },
52 | {
53 | "kind": "SCALAR",
54 | "name": "String",
55 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
56 | "fields": null,
57 | "inputFields": null,
58 | "interfaces": null,
59 | "enumValues": null,
60 | "possibleTypes": null
61 | },
62 | {
63 | "kind": "OBJECT",
64 | "name": "ExchangeRate",
65 | "description": "",
66 | "fields": [
67 | {
68 | "name": "currency",
69 | "description": "",
70 | "args": [],
71 | "type": {
72 | "kind": "SCALAR",
73 | "name": "String",
74 | "ofType": null
75 | },
76 | "isDeprecated": false,
77 | "deprecationReason": null
78 | },
79 | {
80 | "name": "rate",
81 | "description": "",
82 | "args": [],
83 | "type": {
84 | "kind": "SCALAR",
85 | "name": "String",
86 | "ofType": null
87 | },
88 | "isDeprecated": false,
89 | "deprecationReason": null
90 | },
91 | {
92 | "name": "name",
93 | "description": "",
94 | "args": [],
95 | "type": {
96 | "kind": "SCALAR",
97 | "name": "String",
98 | "ofType": null
99 | },
100 | "isDeprecated": false,
101 | "deprecationReason": null
102 | }
103 | ],
104 | "inputFields": null,
105 | "interfaces": [],
106 | "enumValues": null,
107 | "possibleTypes": null
108 | },
109 | {
110 | "kind": "OBJECT",
111 | "name": "__Schema",
112 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
113 | "fields": [
114 | {
115 | "name": "types",
116 | "description": "A list of all types supported by this server.",
117 | "args": [],
118 | "type": {
119 | "kind": "NON_NULL",
120 | "name": null,
121 | "ofType": {
122 | "kind": "LIST",
123 | "name": null,
124 | "ofType": {
125 | "kind": "NON_NULL",
126 | "name": null,
127 | "ofType": {
128 | "kind": "OBJECT",
129 | "name": "__Type",
130 | "ofType": null
131 | }
132 | }
133 | }
134 | },
135 | "isDeprecated": false,
136 | "deprecationReason": null
137 | },
138 | {
139 | "name": "queryType",
140 | "description": "The type that query operations will be rooted at.",
141 | "args": [],
142 | "type": {
143 | "kind": "NON_NULL",
144 | "name": null,
145 | "ofType": {
146 | "kind": "OBJECT",
147 | "name": "__Type",
148 | "ofType": null
149 | }
150 | },
151 | "isDeprecated": false,
152 | "deprecationReason": null
153 | },
154 | {
155 | "name": "mutationType",
156 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
157 | "args": [],
158 | "type": {
159 | "kind": "OBJECT",
160 | "name": "__Type",
161 | "ofType": null
162 | },
163 | "isDeprecated": false,
164 | "deprecationReason": null
165 | },
166 | {
167 | "name": "subscriptionType",
168 | "description": "If this server support subscription, the type that subscription operations will be rooted at.",
169 | "args": [],
170 | "type": {
171 | "kind": "OBJECT",
172 | "name": "__Type",
173 | "ofType": null
174 | },
175 | "isDeprecated": false,
176 | "deprecationReason": null
177 | },
178 | {
179 | "name": "directives",
180 | "description": "A list of all directives supported by this server.",
181 | "args": [],
182 | "type": {
183 | "kind": "NON_NULL",
184 | "name": null,
185 | "ofType": {
186 | "kind": "LIST",
187 | "name": null,
188 | "ofType": {
189 | "kind": "NON_NULL",
190 | "name": null,
191 | "ofType": {
192 | "kind": "OBJECT",
193 | "name": "__Directive",
194 | "ofType": null
195 | }
196 | }
197 | }
198 | },
199 | "isDeprecated": false,
200 | "deprecationReason": null
201 | }
202 | ],
203 | "inputFields": null,
204 | "interfaces": [],
205 | "enumValues": null,
206 | "possibleTypes": null
207 | },
208 | {
209 | "kind": "OBJECT",
210 | "name": "__Type",
211 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.",
212 | "fields": [
213 | {
214 | "name": "kind",
215 | "description": null,
216 | "args": [],
217 | "type": {
218 | "kind": "NON_NULL",
219 | "name": null,
220 | "ofType": {
221 | "kind": "ENUM",
222 | "name": "__TypeKind",
223 | "ofType": null
224 | }
225 | },
226 | "isDeprecated": false,
227 | "deprecationReason": null
228 | },
229 | {
230 | "name": "name",
231 | "description": null,
232 | "args": [],
233 | "type": {
234 | "kind": "SCALAR",
235 | "name": "String",
236 | "ofType": null
237 | },
238 | "isDeprecated": false,
239 | "deprecationReason": null
240 | },
241 | {
242 | "name": "description",
243 | "description": null,
244 | "args": [],
245 | "type": {
246 | "kind": "SCALAR",
247 | "name": "String",
248 | "ofType": null
249 | },
250 | "isDeprecated": false,
251 | "deprecationReason": null
252 | },
253 | {
254 | "name": "fields",
255 | "description": null,
256 | "args": [
257 | {
258 | "name": "includeDeprecated",
259 | "description": null,
260 | "type": {
261 | "kind": "SCALAR",
262 | "name": "Boolean",
263 | "ofType": null
264 | },
265 | "defaultValue": "false"
266 | }
267 | ],
268 | "type": {
269 | "kind": "LIST",
270 | "name": null,
271 | "ofType": {
272 | "kind": "NON_NULL",
273 | "name": null,
274 | "ofType": {
275 | "kind": "OBJECT",
276 | "name": "__Field",
277 | "ofType": null
278 | }
279 | }
280 | },
281 | "isDeprecated": false,
282 | "deprecationReason": null
283 | },
284 | {
285 | "name": "interfaces",
286 | "description": null,
287 | "args": [],
288 | "type": {
289 | "kind": "LIST",
290 | "name": null,
291 | "ofType": {
292 | "kind": "NON_NULL",
293 | "name": null,
294 | "ofType": {
295 | "kind": "OBJECT",
296 | "name": "__Type",
297 | "ofType": null
298 | }
299 | }
300 | },
301 | "isDeprecated": false,
302 | "deprecationReason": null
303 | },
304 | {
305 | "name": "possibleTypes",
306 | "description": null,
307 | "args": [],
308 | "type": {
309 | "kind": "LIST",
310 | "name": null,
311 | "ofType": {
312 | "kind": "NON_NULL",
313 | "name": null,
314 | "ofType": {
315 | "kind": "OBJECT",
316 | "name": "__Type",
317 | "ofType": null
318 | }
319 | }
320 | },
321 | "isDeprecated": false,
322 | "deprecationReason": null
323 | },
324 | {
325 | "name": "enumValues",
326 | "description": null,
327 | "args": [
328 | {
329 | "name": "includeDeprecated",
330 | "description": null,
331 | "type": {
332 | "kind": "SCALAR",
333 | "name": "Boolean",
334 | "ofType": null
335 | },
336 | "defaultValue": "false"
337 | }
338 | ],
339 | "type": {
340 | "kind": "LIST",
341 | "name": null,
342 | "ofType": {
343 | "kind": "NON_NULL",
344 | "name": null,
345 | "ofType": {
346 | "kind": "OBJECT",
347 | "name": "__EnumValue",
348 | "ofType": null
349 | }
350 | }
351 | },
352 | "isDeprecated": false,
353 | "deprecationReason": null
354 | },
355 | {
356 | "name": "inputFields",
357 | "description": null,
358 | "args": [],
359 | "type": {
360 | "kind": "LIST",
361 | "name": null,
362 | "ofType": {
363 | "kind": "NON_NULL",
364 | "name": null,
365 | "ofType": {
366 | "kind": "OBJECT",
367 | "name": "__InputValue",
368 | "ofType": null
369 | }
370 | }
371 | },
372 | "isDeprecated": false,
373 | "deprecationReason": null
374 | },
375 | {
376 | "name": "ofType",
377 | "description": null,
378 | "args": [],
379 | "type": {
380 | "kind": "OBJECT",
381 | "name": "__Type",
382 | "ofType": null
383 | },
384 | "isDeprecated": false,
385 | "deprecationReason": null
386 | }
387 | ],
388 | "inputFields": null,
389 | "interfaces": [],
390 | "enumValues": null,
391 | "possibleTypes": null
392 | },
393 | {
394 | "kind": "ENUM",
395 | "name": "__TypeKind",
396 | "description": "An enum describing what kind of type a given `__Type` is.",
397 | "fields": null,
398 | "inputFields": null,
399 | "interfaces": null,
400 | "enumValues": [
401 | {
402 | "name": "SCALAR",
403 | "description": "Indicates this type is a scalar.",
404 | "isDeprecated": false,
405 | "deprecationReason": null
406 | },
407 | {
408 | "name": "OBJECT",
409 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
410 | "isDeprecated": false,
411 | "deprecationReason": null
412 | },
413 | {
414 | "name": "INTERFACE",
415 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
416 | "isDeprecated": false,
417 | "deprecationReason": null
418 | },
419 | {
420 | "name": "UNION",
421 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
422 | "isDeprecated": false,
423 | "deprecationReason": null
424 | },
425 | {
426 | "name": "ENUM",
427 | "description": "Indicates this type is an enum. `enumValues` is a valid field.",
428 | "isDeprecated": false,
429 | "deprecationReason": null
430 | },
431 | {
432 | "name": "INPUT_OBJECT",
433 | "description": "Indicates this type is an input object. `inputFields` is a valid field.",
434 | "isDeprecated": false,
435 | "deprecationReason": null
436 | },
437 | {
438 | "name": "LIST",
439 | "description": "Indicates this type is a list. `ofType` is a valid field.",
440 | "isDeprecated": false,
441 | "deprecationReason": null
442 | },
443 | {
444 | "name": "NON_NULL",
445 | "description": "Indicates this type is a non-null. `ofType` is a valid field.",
446 | "isDeprecated": false,
447 | "deprecationReason": null
448 | }
449 | ],
450 | "possibleTypes": null
451 | },
452 | {
453 | "kind": "SCALAR",
454 | "name": "Boolean",
455 | "description": "The `Boolean` scalar type represents `true` or `false`.",
456 | "fields": null,
457 | "inputFields": null,
458 | "interfaces": null,
459 | "enumValues": null,
460 | "possibleTypes": null
461 | },
462 | {
463 | "kind": "OBJECT",
464 | "name": "__Field",
465 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
466 | "fields": [
467 | {
468 | "name": "name",
469 | "description": null,
470 | "args": [],
471 | "type": {
472 | "kind": "NON_NULL",
473 | "name": null,
474 | "ofType": {
475 | "kind": "SCALAR",
476 | "name": "String",
477 | "ofType": null
478 | }
479 | },
480 | "isDeprecated": false,
481 | "deprecationReason": null
482 | },
483 | {
484 | "name": "description",
485 | "description": null,
486 | "args": [],
487 | "type": {
488 | "kind": "SCALAR",
489 | "name": "String",
490 | "ofType": null
491 | },
492 | "isDeprecated": false,
493 | "deprecationReason": null
494 | },
495 | {
496 | "name": "args",
497 | "description": null,
498 | "args": [],
499 | "type": {
500 | "kind": "NON_NULL",
501 | "name": null,
502 | "ofType": {
503 | "kind": "LIST",
504 | "name": null,
505 | "ofType": {
506 | "kind": "NON_NULL",
507 | "name": null,
508 | "ofType": {
509 | "kind": "OBJECT",
510 | "name": "__InputValue",
511 | "ofType": null
512 | }
513 | }
514 | }
515 | },
516 | "isDeprecated": false,
517 | "deprecationReason": null
518 | },
519 | {
520 | "name": "type",
521 | "description": null,
522 | "args": [],
523 | "type": {
524 | "kind": "NON_NULL",
525 | "name": null,
526 | "ofType": {
527 | "kind": "OBJECT",
528 | "name": "__Type",
529 | "ofType": null
530 | }
531 | },
532 | "isDeprecated": false,
533 | "deprecationReason": null
534 | },
535 | {
536 | "name": "isDeprecated",
537 | "description": null,
538 | "args": [],
539 | "type": {
540 | "kind": "NON_NULL",
541 | "name": null,
542 | "ofType": {
543 | "kind": "SCALAR",
544 | "name": "Boolean",
545 | "ofType": null
546 | }
547 | },
548 | "isDeprecated": false,
549 | "deprecationReason": null
550 | },
551 | {
552 | "name": "deprecationReason",
553 | "description": null,
554 | "args": [],
555 | "type": {
556 | "kind": "SCALAR",
557 | "name": "String",
558 | "ofType": null
559 | },
560 | "isDeprecated": false,
561 | "deprecationReason": null
562 | }
563 | ],
564 | "inputFields": null,
565 | "interfaces": [],
566 | "enumValues": null,
567 | "possibleTypes": null
568 | },
569 | {
570 | "kind": "OBJECT",
571 | "name": "__InputValue",
572 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
573 | "fields": [
574 | {
575 | "name": "name",
576 | "description": null,
577 | "args": [],
578 | "type": {
579 | "kind": "NON_NULL",
580 | "name": null,
581 | "ofType": {
582 | "kind": "SCALAR",
583 | "name": "String",
584 | "ofType": null
585 | }
586 | },
587 | "isDeprecated": false,
588 | "deprecationReason": null
589 | },
590 | {
591 | "name": "description",
592 | "description": null,
593 | "args": [],
594 | "type": {
595 | "kind": "SCALAR",
596 | "name": "String",
597 | "ofType": null
598 | },
599 | "isDeprecated": false,
600 | "deprecationReason": null
601 | },
602 | {
603 | "name": "type",
604 | "description": null,
605 | "args": [],
606 | "type": {
607 | "kind": "NON_NULL",
608 | "name": null,
609 | "ofType": {
610 | "kind": "OBJECT",
611 | "name": "__Type",
612 | "ofType": null
613 | }
614 | },
615 | "isDeprecated": false,
616 | "deprecationReason": null
617 | },
618 | {
619 | "name": "defaultValue",
620 | "description": "A GraphQL-formatted string representing the default value for this input value.",
621 | "args": [],
622 | "type": {
623 | "kind": "SCALAR",
624 | "name": "String",
625 | "ofType": null
626 | },
627 | "isDeprecated": false,
628 | "deprecationReason": null
629 | }
630 | ],
631 | "inputFields": null,
632 | "interfaces": [],
633 | "enumValues": null,
634 | "possibleTypes": null
635 | },
636 | {
637 | "kind": "OBJECT",
638 | "name": "__EnumValue",
639 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
640 | "fields": [
641 | {
642 | "name": "name",
643 | "description": null,
644 | "args": [],
645 | "type": {
646 | "kind": "NON_NULL",
647 | "name": null,
648 | "ofType": {
649 | "kind": "SCALAR",
650 | "name": "String",
651 | "ofType": null
652 | }
653 | },
654 | "isDeprecated": false,
655 | "deprecationReason": null
656 | },
657 | {
658 | "name": "description",
659 | "description": null,
660 | "args": [],
661 | "type": {
662 | "kind": "SCALAR",
663 | "name": "String",
664 | "ofType": null
665 | },
666 | "isDeprecated": false,
667 | "deprecationReason": null
668 | },
669 | {
670 | "name": "isDeprecated",
671 | "description": null,
672 | "args": [],
673 | "type": {
674 | "kind": "NON_NULL",
675 | "name": null,
676 | "ofType": {
677 | "kind": "SCALAR",
678 | "name": "Boolean",
679 | "ofType": null
680 | }
681 | },
682 | "isDeprecated": false,
683 | "deprecationReason": null
684 | },
685 | {
686 | "name": "deprecationReason",
687 | "description": null,
688 | "args": [],
689 | "type": {
690 | "kind": "SCALAR",
691 | "name": "String",
692 | "ofType": null
693 | },
694 | "isDeprecated": false,
695 | "deprecationReason": null
696 | }
697 | ],
698 | "inputFields": null,
699 | "interfaces": [],
700 | "enumValues": null,
701 | "possibleTypes": null
702 | },
703 | {
704 | "kind": "OBJECT",
705 | "name": "__Directive",
706 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.",
707 | "fields": [
708 | {
709 | "name": "name",
710 | "description": null,
711 | "args": [],
712 | "type": {
713 | "kind": "NON_NULL",
714 | "name": null,
715 | "ofType": {
716 | "kind": "SCALAR",
717 | "name": "String",
718 | "ofType": null
719 | }
720 | },
721 | "isDeprecated": false,
722 | "deprecationReason": null
723 | },
724 | {
725 | "name": "description",
726 | "description": null,
727 | "args": [],
728 | "type": {
729 | "kind": "SCALAR",
730 | "name": "String",
731 | "ofType": null
732 | },
733 | "isDeprecated": false,
734 | "deprecationReason": null
735 | },
736 | {
737 | "name": "locations",
738 | "description": null,
739 | "args": [],
740 | "type": {
741 | "kind": "NON_NULL",
742 | "name": null,
743 | "ofType": {
744 | "kind": "LIST",
745 | "name": null,
746 | "ofType": {
747 | "kind": "NON_NULL",
748 | "name": null,
749 | "ofType": {
750 | "kind": "ENUM",
751 | "name": "__DirectiveLocation",
752 | "ofType": null
753 | }
754 | }
755 | }
756 | },
757 | "isDeprecated": false,
758 | "deprecationReason": null
759 | },
760 | {
761 | "name": "args",
762 | "description": null,
763 | "args": [],
764 | "type": {
765 | "kind": "NON_NULL",
766 | "name": null,
767 | "ofType": {
768 | "kind": "LIST",
769 | "name": null,
770 | "ofType": {
771 | "kind": "NON_NULL",
772 | "name": null,
773 | "ofType": {
774 | "kind": "OBJECT",
775 | "name": "__InputValue",
776 | "ofType": null
777 | }
778 | }
779 | }
780 | },
781 | "isDeprecated": false,
782 | "deprecationReason": null
783 | },
784 | {
785 | "name": "onOperation",
786 | "description": null,
787 | "args": [],
788 | "type": {
789 | "kind": "NON_NULL",
790 | "name": null,
791 | "ofType": {
792 | "kind": "SCALAR",
793 | "name": "Boolean",
794 | "ofType": null
795 | }
796 | },
797 | "isDeprecated": true,
798 | "deprecationReason": "Use `locations`."
799 | },
800 | {
801 | "name": "onFragment",
802 | "description": null,
803 | "args": [],
804 | "type": {
805 | "kind": "NON_NULL",
806 | "name": null,
807 | "ofType": {
808 | "kind": "SCALAR",
809 | "name": "Boolean",
810 | "ofType": null
811 | }
812 | },
813 | "isDeprecated": true,
814 | "deprecationReason": "Use `locations`."
815 | },
816 | {
817 | "name": "onField",
818 | "description": null,
819 | "args": [],
820 | "type": {
821 | "kind": "NON_NULL",
822 | "name": null,
823 | "ofType": {
824 | "kind": "SCALAR",
825 | "name": "Boolean",
826 | "ofType": null
827 | }
828 | },
829 | "isDeprecated": true,
830 | "deprecationReason": "Use `locations`."
831 | }
832 | ],
833 | "inputFields": null,
834 | "interfaces": [],
835 | "enumValues": null,
836 | "possibleTypes": null
837 | },
838 | {
839 | "kind": "ENUM",
840 | "name": "__DirectiveLocation",
841 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.",
842 | "fields": null,
843 | "inputFields": null,
844 | "interfaces": null,
845 | "enumValues": [
846 | {
847 | "name": "QUERY",
848 | "description": "Location adjacent to a query operation.",
849 | "isDeprecated": false,
850 | "deprecationReason": null
851 | },
852 | {
853 | "name": "MUTATION",
854 | "description": "Location adjacent to a mutation operation.",
855 | "isDeprecated": false,
856 | "deprecationReason": null
857 | },
858 | {
859 | "name": "SUBSCRIPTION",
860 | "description": "Location adjacent to a subscription operation.",
861 | "isDeprecated": false,
862 | "deprecationReason": null
863 | },
864 | {
865 | "name": "FIELD",
866 | "description": "Location adjacent to a field.",
867 | "isDeprecated": false,
868 | "deprecationReason": null
869 | },
870 | {
871 | "name": "FRAGMENT_DEFINITION",
872 | "description": "Location adjacent to a fragment definition.",
873 | "isDeprecated": false,
874 | "deprecationReason": null
875 | },
876 | {
877 | "name": "FRAGMENT_SPREAD",
878 | "description": "Location adjacent to a fragment spread.",
879 | "isDeprecated": false,
880 | "deprecationReason": null
881 | },
882 | {
883 | "name": "INLINE_FRAGMENT",
884 | "description": "Location adjacent to an inline fragment.",
885 | "isDeprecated": false,
886 | "deprecationReason": null
887 | },
888 | {
889 | "name": "SCHEMA",
890 | "description": "Location adjacent to a schema definition.",
891 | "isDeprecated": false,
892 | "deprecationReason": null
893 | },
894 | {
895 | "name": "SCALAR",
896 | "description": "Location adjacent to a scalar definition.",
897 | "isDeprecated": false,
898 | "deprecationReason": null
899 | },
900 | {
901 | "name": "OBJECT",
902 | "description": "Location adjacent to an object type definition.",
903 | "isDeprecated": false,
904 | "deprecationReason": null
905 | },
906 | {
907 | "name": "FIELD_DEFINITION",
908 | "description": "Location adjacent to a field definition.",
909 | "isDeprecated": false,
910 | "deprecationReason": null
911 | },
912 | {
913 | "name": "ARGUMENT_DEFINITION",
914 | "description": "Location adjacent to an argument definition.",
915 | "isDeprecated": false,
916 | "deprecationReason": null
917 | },
918 | {
919 | "name": "INTERFACE",
920 | "description": "Location adjacent to an interface definition.",
921 | "isDeprecated": false,
922 | "deprecationReason": null
923 | },
924 | {
925 | "name": "UNION",
926 | "description": "Location adjacent to a union definition.",
927 | "isDeprecated": false,
928 | "deprecationReason": null
929 | },
930 | {
931 | "name": "ENUM",
932 | "description": "Location adjacent to an enum definition.",
933 | "isDeprecated": false,
934 | "deprecationReason": null
935 | },
936 | {
937 | "name": "ENUM_VALUE",
938 | "description": "Location adjacent to an enum value definition.",
939 | "isDeprecated": false,
940 | "deprecationReason": null
941 | },
942 | {
943 | "name": "INPUT_OBJECT",
944 | "description": "Location adjacent to an input object type definition.",
945 | "isDeprecated": false,
946 | "deprecationReason": null
947 | },
948 | {
949 | "name": "INPUT_FIELD_DEFINITION",
950 | "description": "Location adjacent to an input object field definition.",
951 | "isDeprecated": false,
952 | "deprecationReason": null
953 | }
954 | ],
955 | "possibleTypes": null
956 | }
957 | ],
958 | "directives": [
959 | {
960 | "name": "skip",
961 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
962 | "locations": [
963 | "FIELD",
964 | "FRAGMENT_SPREAD",
965 | "INLINE_FRAGMENT"
966 | ],
967 | "args": [
968 | {
969 | "name": "if",
970 | "description": "Skipped when true.",
971 | "type": {
972 | "kind": "NON_NULL",
973 | "name": null,
974 | "ofType": {
975 | "kind": "SCALAR",
976 | "name": "Boolean",
977 | "ofType": null
978 | }
979 | },
980 | "defaultValue": null
981 | }
982 | ]
983 | },
984 | {
985 | "name": "include",
986 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.",
987 | "locations": [
988 | "FIELD",
989 | "FRAGMENT_SPREAD",
990 | "INLINE_FRAGMENT"
991 | ],
992 | "args": [
993 | {
994 | "name": "if",
995 | "description": "Included when true.",
996 | "type": {
997 | "kind": "NON_NULL",
998 | "name": null,
999 | "ofType": {
1000 | "kind": "SCALAR",
1001 | "name": "Boolean",
1002 | "ofType": null
1003 | }
1004 | },
1005 | "defaultValue": null
1006 | }
1007 | ]
1008 | },
1009 | {
1010 | "name": "deprecated",
1011 | "description": "Marks an element of a GraphQL schema as no longer supported.",
1012 | "locations": [
1013 | "FIELD_DEFINITION",
1014 | "ENUM_VALUE"
1015 | ],
1016 | "args": [
1017 | {
1018 | "name": "reason",
1019 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).",
1020 | "type": {
1021 | "kind": "SCALAR",
1022 | "name": "String",
1023 | "ofType": null
1024 | },
1025 | "defaultValue": "\"No longer supported\""
1026 | }
1027 | ]
1028 | }
1029 | ]
1030 | }
1031 | },
1032 | "extensions": {
1033 | "tracing": {
1034 | "version": 1,
1035 | "startTime": "2018-03-24T21:01:39.679Z",
1036 | "endTime": "2018-03-24T21:01:39.722Z",
1037 | "duration": 42687883,
1038 | "execution": {
1039 | "resolvers": []
1040 | }
1041 | }
1042 | }
1043 | }
--------------------------------------------------------------------------------
/example/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "__schema": {
3 | "description": null,
4 | "queryType": {
5 | "name": "Query"
6 | },
7 | "mutationType": {
8 | "name": "Mutation"
9 | },
10 | "subscriptionType": null,
11 | "types": [
12 | {
13 | "kind": "OBJECT",
14 | "name": "Query",
15 | "description": "",
16 | "fields": [
17 | {
18 | "name": "todos",
19 | "description": "",
20 | "args": [],
21 | "type": {
22 | "kind": "LIST",
23 | "name": null,
24 | "ofType": {
25 | "kind": "OBJECT",
26 | "name": "Todo",
27 | "ofType": null
28 | }
29 | },
30 | "isDeprecated": false,
31 | "deprecationReason": null
32 | },
33 | {
34 | "name": "todo",
35 | "description": "",
36 | "args": [
37 | {
38 | "name": "id",
39 | "description": "",
40 | "type": {
41 | "kind": "NON_NULL",
42 | "name": null,
43 | "ofType": {
44 | "kind": "SCALAR",
45 | "name": "String",
46 | "ofType": null
47 | }
48 | },
49 | "defaultValue": null
50 | }
51 | ],
52 | "type": {
53 | "kind": "OBJECT",
54 | "name": "Todo",
55 | "ofType": null
56 | },
57 | "isDeprecated": false,
58 | "deprecationReason": null
59 | }
60 | ],
61 | "inputFields": null,
62 | "interfaces": [],
63 | "enumValues": null,
64 | "possibleTypes": null
65 | },
66 | {
67 | "kind": "OBJECT",
68 | "name": "Todo",
69 | "description": "",
70 | "fields": [
71 | {
72 | "name": "id",
73 | "description": "",
74 | "args": [],
75 | "type": {
76 | "kind": "NON_NULL",
77 | "name": null,
78 | "ofType": {
79 | "kind": "SCALAR",
80 | "name": "String",
81 | "ofType": null
82 | }
83 | },
84 | "isDeprecated": false,
85 | "deprecationReason": null
86 | },
87 | {
88 | "name": "type",
89 | "description": "",
90 | "args": [],
91 | "type": {
92 | "kind": "NON_NULL",
93 | "name": null,
94 | "ofType": {
95 | "kind": "SCALAR",
96 | "name": "String",
97 | "ofType": null
98 | }
99 | },
100 | "isDeprecated": false,
101 | "deprecationReason": null
102 | }
103 | ],
104 | "inputFields": null,
105 | "interfaces": [],
106 | "enumValues": null,
107 | "possibleTypes": null
108 | },
109 | {
110 | "kind": "SCALAR",
111 | "name": "String",
112 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
113 | "fields": null,
114 | "inputFields": null,
115 | "interfaces": null,
116 | "enumValues": null,
117 | "possibleTypes": null
118 | },
119 | {
120 | "kind": "OBJECT",
121 | "name": "Mutation",
122 | "description": "",
123 | "fields": [
124 | {
125 | "name": "addTodo",
126 | "description": "",
127 | "args": [
128 | {
129 | "name": "type",
130 | "description": "",
131 | "type": {
132 | "kind": "NON_NULL",
133 | "name": null,
134 | "ofType": {
135 | "kind": "SCALAR",
136 | "name": "String",
137 | "ofType": null
138 | }
139 | },
140 | "defaultValue": null
141 | }
142 | ],
143 | "type": {
144 | "kind": "OBJECT",
145 | "name": "Todo",
146 | "ofType": null
147 | },
148 | "isDeprecated": false,
149 | "deprecationReason": null
150 | },
151 | {
152 | "name": "updateTodo",
153 | "description": "",
154 | "args": [
155 | {
156 | "name": "id",
157 | "description": "",
158 | "type": {
159 | "kind": "NON_NULL",
160 | "name": null,
161 | "ofType": {
162 | "kind": "SCALAR",
163 | "name": "String",
164 | "ofType": null
165 | }
166 | },
167 | "defaultValue": null
168 | },
169 | {
170 | "name": "type",
171 | "description": "",
172 | "type": {
173 | "kind": "NON_NULL",
174 | "name": null,
175 | "ofType": {
176 | "kind": "SCALAR",
177 | "name": "String",
178 | "ofType": null
179 | }
180 | },
181 | "defaultValue": null
182 | }
183 | ],
184 | "type": {
185 | "kind": "OBJECT",
186 | "name": "Todo",
187 | "ofType": null
188 | },
189 | "isDeprecated": false,
190 | "deprecationReason": null
191 | }
192 | ],
193 | "inputFields": null,
194 | "interfaces": [],
195 | "enumValues": null,
196 | "possibleTypes": null
197 | },
198 | {
199 | "kind": "OBJECT",
200 | "name": "__Schema",
201 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
202 | "fields": [
203 | {
204 | "name": "description",
205 | "description": null,
206 | "args": [],
207 | "type": {
208 | "kind": "SCALAR",
209 | "name": "String",
210 | "ofType": null
211 | },
212 | "isDeprecated": false,
213 | "deprecationReason": null
214 | },
215 | {
216 | "name": "types",
217 | "description": "A list of all types supported by this server.",
218 | "args": [],
219 | "type": {
220 | "kind": "NON_NULL",
221 | "name": null,
222 | "ofType": {
223 | "kind": "LIST",
224 | "name": null,
225 | "ofType": {
226 | "kind": "NON_NULL",
227 | "name": null,
228 | "ofType": {
229 | "kind": "OBJECT",
230 | "name": "__Type",
231 | "ofType": null
232 | }
233 | }
234 | }
235 | },
236 | "isDeprecated": false,
237 | "deprecationReason": null
238 | },
239 | {
240 | "name": "queryType",
241 | "description": "The type that query operations will be rooted at.",
242 | "args": [],
243 | "type": {
244 | "kind": "NON_NULL",
245 | "name": null,
246 | "ofType": {
247 | "kind": "OBJECT",
248 | "name": "__Type",
249 | "ofType": null
250 | }
251 | },
252 | "isDeprecated": false,
253 | "deprecationReason": null
254 | },
255 | {
256 | "name": "mutationType",
257 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
258 | "args": [],
259 | "type": {
260 | "kind": "OBJECT",
261 | "name": "__Type",
262 | "ofType": null
263 | },
264 | "isDeprecated": false,
265 | "deprecationReason": null
266 | },
267 | {
268 | "name": "subscriptionType",
269 | "description": "If this server support subscription, the type that subscription operations will be rooted at.",
270 | "args": [],
271 | "type": {
272 | "kind": "OBJECT",
273 | "name": "__Type",
274 | "ofType": null
275 | },
276 | "isDeprecated": false,
277 | "deprecationReason": null
278 | },
279 | {
280 | "name": "directives",
281 | "description": "A list of all directives supported by this server.",
282 | "args": [],
283 | "type": {
284 | "kind": "NON_NULL",
285 | "name": null,
286 | "ofType": {
287 | "kind": "LIST",
288 | "name": null,
289 | "ofType": {
290 | "kind": "NON_NULL",
291 | "name": null,
292 | "ofType": {
293 | "kind": "OBJECT",
294 | "name": "__Directive",
295 | "ofType": null
296 | }
297 | }
298 | }
299 | },
300 | "isDeprecated": false,
301 | "deprecationReason": null
302 | }
303 | ],
304 | "inputFields": null,
305 | "interfaces": [],
306 | "enumValues": null,
307 | "possibleTypes": null
308 | },
309 | {
310 | "kind": "OBJECT",
311 | "name": "__Type",
312 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByUrl`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.",
313 | "fields": [
314 | {
315 | "name": "kind",
316 | "description": null,
317 | "args": [],
318 | "type": {
319 | "kind": "NON_NULL",
320 | "name": null,
321 | "ofType": {
322 | "kind": "ENUM",
323 | "name": "__TypeKind",
324 | "ofType": null
325 | }
326 | },
327 | "isDeprecated": false,
328 | "deprecationReason": null
329 | },
330 | {
331 | "name": "name",
332 | "description": null,
333 | "args": [],
334 | "type": {
335 | "kind": "SCALAR",
336 | "name": "String",
337 | "ofType": null
338 | },
339 | "isDeprecated": false,
340 | "deprecationReason": null
341 | },
342 | {
343 | "name": "description",
344 | "description": null,
345 | "args": [],
346 | "type": {
347 | "kind": "SCALAR",
348 | "name": "String",
349 | "ofType": null
350 | },
351 | "isDeprecated": false,
352 | "deprecationReason": null
353 | },
354 | {
355 | "name": "specifiedByUrl",
356 | "description": null,
357 | "args": [],
358 | "type": {
359 | "kind": "SCALAR",
360 | "name": "String",
361 | "ofType": null
362 | },
363 | "isDeprecated": false,
364 | "deprecationReason": null
365 | },
366 | {
367 | "name": "fields",
368 | "description": null,
369 | "args": [
370 | {
371 | "name": "includeDeprecated",
372 | "description": null,
373 | "type": {
374 | "kind": "SCALAR",
375 | "name": "Boolean",
376 | "ofType": null
377 | },
378 | "defaultValue": "false"
379 | }
380 | ],
381 | "type": {
382 | "kind": "LIST",
383 | "name": null,
384 | "ofType": {
385 | "kind": "NON_NULL",
386 | "name": null,
387 | "ofType": {
388 | "kind": "OBJECT",
389 | "name": "__Field",
390 | "ofType": null
391 | }
392 | }
393 | },
394 | "isDeprecated": false,
395 | "deprecationReason": null
396 | },
397 | {
398 | "name": "interfaces",
399 | "description": null,
400 | "args": [],
401 | "type": {
402 | "kind": "LIST",
403 | "name": null,
404 | "ofType": {
405 | "kind": "NON_NULL",
406 | "name": null,
407 | "ofType": {
408 | "kind": "OBJECT",
409 | "name": "__Type",
410 | "ofType": null
411 | }
412 | }
413 | },
414 | "isDeprecated": false,
415 | "deprecationReason": null
416 | },
417 | {
418 | "name": "possibleTypes",
419 | "description": null,
420 | "args": [],
421 | "type": {
422 | "kind": "LIST",
423 | "name": null,
424 | "ofType": {
425 | "kind": "NON_NULL",
426 | "name": null,
427 | "ofType": {
428 | "kind": "OBJECT",
429 | "name": "__Type",
430 | "ofType": null
431 | }
432 | }
433 | },
434 | "isDeprecated": false,
435 | "deprecationReason": null
436 | },
437 | {
438 | "name": "enumValues",
439 | "description": null,
440 | "args": [
441 | {
442 | "name": "includeDeprecated",
443 | "description": null,
444 | "type": {
445 | "kind": "SCALAR",
446 | "name": "Boolean",
447 | "ofType": null
448 | },
449 | "defaultValue": "false"
450 | }
451 | ],
452 | "type": {
453 | "kind": "LIST",
454 | "name": null,
455 | "ofType": {
456 | "kind": "NON_NULL",
457 | "name": null,
458 | "ofType": {
459 | "kind": "OBJECT",
460 | "name": "__EnumValue",
461 | "ofType": null
462 | }
463 | }
464 | },
465 | "isDeprecated": false,
466 | "deprecationReason": null
467 | },
468 | {
469 | "name": "inputFields",
470 | "description": null,
471 | "args": [],
472 | "type": {
473 | "kind": "LIST",
474 | "name": null,
475 | "ofType": {
476 | "kind": "NON_NULL",
477 | "name": null,
478 | "ofType": {
479 | "kind": "OBJECT",
480 | "name": "__InputValue",
481 | "ofType": null
482 | }
483 | }
484 | },
485 | "isDeprecated": false,
486 | "deprecationReason": null
487 | },
488 | {
489 | "name": "ofType",
490 | "description": null,
491 | "args": [],
492 | "type": {
493 | "kind": "OBJECT",
494 | "name": "__Type",
495 | "ofType": null
496 | },
497 | "isDeprecated": false,
498 | "deprecationReason": null
499 | }
500 | ],
501 | "inputFields": null,
502 | "interfaces": [],
503 | "enumValues": null,
504 | "possibleTypes": null
505 | },
506 | {
507 | "kind": "ENUM",
508 | "name": "__TypeKind",
509 | "description": "An enum describing what kind of type a given `__Type` is.",
510 | "fields": null,
511 | "inputFields": null,
512 | "interfaces": null,
513 | "enumValues": [
514 | {
515 | "name": "SCALAR",
516 | "description": "Indicates this type is a scalar.",
517 | "isDeprecated": false,
518 | "deprecationReason": null
519 | },
520 | {
521 | "name": "OBJECT",
522 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
523 | "isDeprecated": false,
524 | "deprecationReason": null
525 | },
526 | {
527 | "name": "INTERFACE",
528 | "description": "Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields.",
529 | "isDeprecated": false,
530 | "deprecationReason": null
531 | },
532 | {
533 | "name": "UNION",
534 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
535 | "isDeprecated": false,
536 | "deprecationReason": null
537 | },
538 | {
539 | "name": "ENUM",
540 | "description": "Indicates this type is an enum. `enumValues` is a valid field.",
541 | "isDeprecated": false,
542 | "deprecationReason": null
543 | },
544 | {
545 | "name": "INPUT_OBJECT",
546 | "description": "Indicates this type is an input object. `inputFields` is a valid field.",
547 | "isDeprecated": false,
548 | "deprecationReason": null
549 | },
550 | {
551 | "name": "LIST",
552 | "description": "Indicates this type is a list. `ofType` is a valid field.",
553 | "isDeprecated": false,
554 | "deprecationReason": null
555 | },
556 | {
557 | "name": "NON_NULL",
558 | "description": "Indicates this type is a non-null. `ofType` is a valid field.",
559 | "isDeprecated": false,
560 | "deprecationReason": null
561 | }
562 | ],
563 | "possibleTypes": null
564 | },
565 | {
566 | "kind": "SCALAR",
567 | "name": "Boolean",
568 | "description": "The `Boolean` scalar type represents `true` or `false`.",
569 | "fields": null,
570 | "inputFields": null,
571 | "interfaces": null,
572 | "enumValues": null,
573 | "possibleTypes": null
574 | },
575 | {
576 | "kind": "OBJECT",
577 | "name": "__Field",
578 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
579 | "fields": [
580 | {
581 | "name": "name",
582 | "description": null,
583 | "args": [],
584 | "type": {
585 | "kind": "NON_NULL",
586 | "name": null,
587 | "ofType": {
588 | "kind": "SCALAR",
589 | "name": "String",
590 | "ofType": null
591 | }
592 | },
593 | "isDeprecated": false,
594 | "deprecationReason": null
595 | },
596 | {
597 | "name": "description",
598 | "description": null,
599 | "args": [],
600 | "type": {
601 | "kind": "SCALAR",
602 | "name": "String",
603 | "ofType": null
604 | },
605 | "isDeprecated": false,
606 | "deprecationReason": null
607 | },
608 | {
609 | "name": "args",
610 | "description": null,
611 | "args": [],
612 | "type": {
613 | "kind": "NON_NULL",
614 | "name": null,
615 | "ofType": {
616 | "kind": "LIST",
617 | "name": null,
618 | "ofType": {
619 | "kind": "NON_NULL",
620 | "name": null,
621 | "ofType": {
622 | "kind": "OBJECT",
623 | "name": "__InputValue",
624 | "ofType": null
625 | }
626 | }
627 | }
628 | },
629 | "isDeprecated": false,
630 | "deprecationReason": null
631 | },
632 | {
633 | "name": "type",
634 | "description": null,
635 | "args": [],
636 | "type": {
637 | "kind": "NON_NULL",
638 | "name": null,
639 | "ofType": {
640 | "kind": "OBJECT",
641 | "name": "__Type",
642 | "ofType": null
643 | }
644 | },
645 | "isDeprecated": false,
646 | "deprecationReason": null
647 | },
648 | {
649 | "name": "isDeprecated",
650 | "description": null,
651 | "args": [],
652 | "type": {
653 | "kind": "NON_NULL",
654 | "name": null,
655 | "ofType": {
656 | "kind": "SCALAR",
657 | "name": "Boolean",
658 | "ofType": null
659 | }
660 | },
661 | "isDeprecated": false,
662 | "deprecationReason": null
663 | },
664 | {
665 | "name": "deprecationReason",
666 | "description": null,
667 | "args": [],
668 | "type": {
669 | "kind": "SCALAR",
670 | "name": "String",
671 | "ofType": null
672 | },
673 | "isDeprecated": false,
674 | "deprecationReason": null
675 | }
676 | ],
677 | "inputFields": null,
678 | "interfaces": [],
679 | "enumValues": null,
680 | "possibleTypes": null
681 | },
682 | {
683 | "kind": "OBJECT",
684 | "name": "__InputValue",
685 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
686 | "fields": [
687 | {
688 | "name": "name",
689 | "description": null,
690 | "args": [],
691 | "type": {
692 | "kind": "NON_NULL",
693 | "name": null,
694 | "ofType": {
695 | "kind": "SCALAR",
696 | "name": "String",
697 | "ofType": null
698 | }
699 | },
700 | "isDeprecated": false,
701 | "deprecationReason": null
702 | },
703 | {
704 | "name": "description",
705 | "description": null,
706 | "args": [],
707 | "type": {
708 | "kind": "SCALAR",
709 | "name": "String",
710 | "ofType": null
711 | },
712 | "isDeprecated": false,
713 | "deprecationReason": null
714 | },
715 | {
716 | "name": "type",
717 | "description": null,
718 | "args": [],
719 | "type": {
720 | "kind": "NON_NULL",
721 | "name": null,
722 | "ofType": {
723 | "kind": "OBJECT",
724 | "name": "__Type",
725 | "ofType": null
726 | }
727 | },
728 | "isDeprecated": false,
729 | "deprecationReason": null
730 | },
731 | {
732 | "name": "defaultValue",
733 | "description": "A GraphQL-formatted string representing the default value for this input value.",
734 | "args": [],
735 | "type": {
736 | "kind": "SCALAR",
737 | "name": "String",
738 | "ofType": null
739 | },
740 | "isDeprecated": false,
741 | "deprecationReason": null
742 | }
743 | ],
744 | "inputFields": null,
745 | "interfaces": [],
746 | "enumValues": null,
747 | "possibleTypes": null
748 | },
749 | {
750 | "kind": "OBJECT",
751 | "name": "__EnumValue",
752 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
753 | "fields": [
754 | {
755 | "name": "name",
756 | "description": null,
757 | "args": [],
758 | "type": {
759 | "kind": "NON_NULL",
760 | "name": null,
761 | "ofType": {
762 | "kind": "SCALAR",
763 | "name": "String",
764 | "ofType": null
765 | }
766 | },
767 | "isDeprecated": false,
768 | "deprecationReason": null
769 | },
770 | {
771 | "name": "description",
772 | "description": null,
773 | "args": [],
774 | "type": {
775 | "kind": "SCALAR",
776 | "name": "String",
777 | "ofType": null
778 | },
779 | "isDeprecated": false,
780 | "deprecationReason": null
781 | },
782 | {
783 | "name": "isDeprecated",
784 | "description": null,
785 | "args": [],
786 | "type": {
787 | "kind": "NON_NULL",
788 | "name": null,
789 | "ofType": {
790 | "kind": "SCALAR",
791 | "name": "Boolean",
792 | "ofType": null
793 | }
794 | },
795 | "isDeprecated": false,
796 | "deprecationReason": null
797 | },
798 | {
799 | "name": "deprecationReason",
800 | "description": null,
801 | "args": [],
802 | "type": {
803 | "kind": "SCALAR",
804 | "name": "String",
805 | "ofType": null
806 | },
807 | "isDeprecated": false,
808 | "deprecationReason": null
809 | }
810 | ],
811 | "inputFields": null,
812 | "interfaces": [],
813 | "enumValues": null,
814 | "possibleTypes": null
815 | },
816 | {
817 | "kind": "OBJECT",
818 | "name": "__Directive",
819 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.",
820 | "fields": [
821 | {
822 | "name": "name",
823 | "description": null,
824 | "args": [],
825 | "type": {
826 | "kind": "NON_NULL",
827 | "name": null,
828 | "ofType": {
829 | "kind": "SCALAR",
830 | "name": "String",
831 | "ofType": null
832 | }
833 | },
834 | "isDeprecated": false,
835 | "deprecationReason": null
836 | },
837 | {
838 | "name": "description",
839 | "description": null,
840 | "args": [],
841 | "type": {
842 | "kind": "SCALAR",
843 | "name": "String",
844 | "ofType": null
845 | },
846 | "isDeprecated": false,
847 | "deprecationReason": null
848 | },
849 | {
850 | "name": "isRepeatable",
851 | "description": null,
852 | "args": [],
853 | "type": {
854 | "kind": "NON_NULL",
855 | "name": null,
856 | "ofType": {
857 | "kind": "SCALAR",
858 | "name": "Boolean",
859 | "ofType": null
860 | }
861 | },
862 | "isDeprecated": false,
863 | "deprecationReason": null
864 | },
865 | {
866 | "name": "locations",
867 | "description": null,
868 | "args": [],
869 | "type": {
870 | "kind": "NON_NULL",
871 | "name": null,
872 | "ofType": {
873 | "kind": "LIST",
874 | "name": null,
875 | "ofType": {
876 | "kind": "NON_NULL",
877 | "name": null,
878 | "ofType": {
879 | "kind": "ENUM",
880 | "name": "__DirectiveLocation",
881 | "ofType": null
882 | }
883 | }
884 | }
885 | },
886 | "isDeprecated": false,
887 | "deprecationReason": null
888 | },
889 | {
890 | "name": "args",
891 | "description": null,
892 | "args": [],
893 | "type": {
894 | "kind": "NON_NULL",
895 | "name": null,
896 | "ofType": {
897 | "kind": "LIST",
898 | "name": null,
899 | "ofType": {
900 | "kind": "NON_NULL",
901 | "name": null,
902 | "ofType": {
903 | "kind": "OBJECT",
904 | "name": "__InputValue",
905 | "ofType": null
906 | }
907 | }
908 | }
909 | },
910 | "isDeprecated": false,
911 | "deprecationReason": null
912 | }
913 | ],
914 | "inputFields": null,
915 | "interfaces": [],
916 | "enumValues": null,
917 | "possibleTypes": null
918 | },
919 | {
920 | "kind": "ENUM",
921 | "name": "__DirectiveLocation",
922 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.",
923 | "fields": null,
924 | "inputFields": null,
925 | "interfaces": null,
926 | "enumValues": [
927 | {
928 | "name": "QUERY",
929 | "description": "Location adjacent to a query operation.",
930 | "isDeprecated": false,
931 | "deprecationReason": null
932 | },
933 | {
934 | "name": "MUTATION",
935 | "description": "Location adjacent to a mutation operation.",
936 | "isDeprecated": false,
937 | "deprecationReason": null
938 | },
939 | {
940 | "name": "SUBSCRIPTION",
941 | "description": "Location adjacent to a subscription operation.",
942 | "isDeprecated": false,
943 | "deprecationReason": null
944 | },
945 | {
946 | "name": "FIELD",
947 | "description": "Location adjacent to a field.",
948 | "isDeprecated": false,
949 | "deprecationReason": null
950 | },
951 | {
952 | "name": "FRAGMENT_DEFINITION",
953 | "description": "Location adjacent to a fragment definition.",
954 | "isDeprecated": false,
955 | "deprecationReason": null
956 | },
957 | {
958 | "name": "FRAGMENT_SPREAD",
959 | "description": "Location adjacent to a fragment spread.",
960 | "isDeprecated": false,
961 | "deprecationReason": null
962 | },
963 | {
964 | "name": "INLINE_FRAGMENT",
965 | "description": "Location adjacent to an inline fragment.",
966 | "isDeprecated": false,
967 | "deprecationReason": null
968 | },
969 | {
970 | "name": "VARIABLE_DEFINITION",
971 | "description": "Location adjacent to a variable definition.",
972 | "isDeprecated": false,
973 | "deprecationReason": null
974 | },
975 | {
976 | "name": "SCHEMA",
977 | "description": "Location adjacent to a schema definition.",
978 | "isDeprecated": false,
979 | "deprecationReason": null
980 | },
981 | {
982 | "name": "SCALAR",
983 | "description": "Location adjacent to a scalar definition.",
984 | "isDeprecated": false,
985 | "deprecationReason": null
986 | },
987 | {
988 | "name": "OBJECT",
989 | "description": "Location adjacent to an object type definition.",
990 | "isDeprecated": false,
991 | "deprecationReason": null
992 | },
993 | {
994 | "name": "FIELD_DEFINITION",
995 | "description": "Location adjacent to a field definition.",
996 | "isDeprecated": false,
997 | "deprecationReason": null
998 | },
999 | {
1000 | "name": "ARGUMENT_DEFINITION",
1001 | "description": "Location adjacent to an argument definition.",
1002 | "isDeprecated": false,
1003 | "deprecationReason": null
1004 | },
1005 | {
1006 | "name": "INTERFACE",
1007 | "description": "Location adjacent to an interface definition.",
1008 | "isDeprecated": false,
1009 | "deprecationReason": null
1010 | },
1011 | {
1012 | "name": "UNION",
1013 | "description": "Location adjacent to a union definition.",
1014 | "isDeprecated": false,
1015 | "deprecationReason": null
1016 | },
1017 | {
1018 | "name": "ENUM",
1019 | "description": "Location adjacent to an enum definition.",
1020 | "isDeprecated": false,
1021 | "deprecationReason": null
1022 | },
1023 | {
1024 | "name": "ENUM_VALUE",
1025 | "description": "Location adjacent to an enum value definition.",
1026 | "isDeprecated": false,
1027 | "deprecationReason": null
1028 | },
1029 | {
1030 | "name": "INPUT_OBJECT",
1031 | "description": "Location adjacent to an input object type definition.",
1032 | "isDeprecated": false,
1033 | "deprecationReason": null
1034 | },
1035 | {
1036 | "name": "INPUT_FIELD_DEFINITION",
1037 | "description": "Location adjacent to an input object field definition.",
1038 | "isDeprecated": false,
1039 | "deprecationReason": null
1040 | }
1041 | ],
1042 | "possibleTypes": null
1043 | },
1044 | {
1045 | "kind": "SCALAR",
1046 | "name": "Upload",
1047 | "description": "The `Upload` scalar type represents a file upload promise that resolves an object containing `stream`, `filename`, `mimetype` and `encoding`.",
1048 | "fields": null,
1049 | "inputFields": null,
1050 | "interfaces": null,
1051 | "enumValues": null,
1052 | "possibleTypes": null
1053 | }
1054 | ],
1055 | "directives": [
1056 | {
1057 | "name": "skip",
1058 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
1059 | "isRepeatable": false,
1060 | "locations": [
1061 | "FIELD",
1062 | "FRAGMENT_SPREAD",
1063 | "INLINE_FRAGMENT"
1064 | ],
1065 | "args": [
1066 | {
1067 | "name": "if",
1068 | "description": "Skipped when true.",
1069 | "type": {
1070 | "kind": "NON_NULL",
1071 | "name": null,
1072 | "ofType": {
1073 | "kind": "SCALAR",
1074 | "name": "Boolean",
1075 | "ofType": null
1076 | }
1077 | },
1078 | "defaultValue": null
1079 | }
1080 | ]
1081 | },
1082 | {
1083 | "name": "include",
1084 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.",
1085 | "isRepeatable": false,
1086 | "locations": [
1087 | "FIELD",
1088 | "FRAGMENT_SPREAD",
1089 | "INLINE_FRAGMENT"
1090 | ],
1091 | "args": [
1092 | {
1093 | "name": "if",
1094 | "description": "Included when true.",
1095 | "type": {
1096 | "kind": "NON_NULL",
1097 | "name": null,
1098 | "ofType": {
1099 | "kind": "SCALAR",
1100 | "name": "Boolean",
1101 | "ofType": null
1102 | }
1103 | },
1104 | "defaultValue": null
1105 | }
1106 | ]
1107 | },
1108 | {
1109 | "name": "deprecated",
1110 | "description": "Marks an element of a GraphQL schema as no longer supported.",
1111 | "isRepeatable": false,
1112 | "locations": [
1113 | "FIELD_DEFINITION",
1114 | "ENUM_VALUE"
1115 | ],
1116 | "args": [
1117 | {
1118 | "name": "reason",
1119 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).",
1120 | "type": {
1121 | "kind": "SCALAR",
1122 | "name": "String",
1123 | "ofType": null
1124 | },
1125 | "defaultValue": "\"No longer supported\""
1126 | }
1127 | ]
1128 | }
1129 | ]
1130 | }
1131 | }
--------------------------------------------------------------------------------
/tests/mutations.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "__schema": {
4 | "queryType": {
5 | "name": "Query"
6 | },
7 | "mutationType": {
8 | "name": "Mutation"
9 | },
10 | "subscriptionType": null,
11 | "types": [
12 | {
13 | "kind": "OBJECT",
14 | "name": "Query",
15 | "description": "",
16 | "fields": [
17 | {
18 | "name": "todos",
19 | "description": "",
20 | "args": [],
21 | "type": {
22 | "kind": "LIST",
23 | "name": null,
24 | "ofType": {
25 | "kind": "OBJECT",
26 | "name": "Todo",
27 | "ofType": null
28 | }
29 | },
30 | "isDeprecated": false,
31 | "deprecationReason": null
32 | },
33 | {
34 | "name": "todo",
35 | "description": "",
36 | "args": [
37 | {
38 | "name": "id",
39 | "description": "",
40 | "type": {
41 | "kind": "NON_NULL",
42 | "name": null,
43 | "ofType": {
44 | "kind": "SCALAR",
45 | "name": "String",
46 | "ofType": null
47 | }
48 | },
49 | "defaultValue": null
50 | }
51 | ],
52 | "type": {
53 | "kind": "OBJECT",
54 | "name": "Todo",
55 | "ofType": null
56 | },
57 | "isDeprecated": false,
58 | "deprecationReason": null
59 | }
60 | ],
61 | "inputFields": null,
62 | "interfaces": [],
63 | "enumValues": null,
64 | "possibleTypes": null
65 | },
66 | {
67 | "kind": "OBJECT",
68 | "name": "Todo",
69 | "description": "",
70 | "fields": [
71 | {
72 | "name": "id",
73 | "description": "",
74 | "args": [],
75 | "type": {
76 | "kind": "NON_NULL",
77 | "name": null,
78 | "ofType": {
79 | "kind": "SCALAR",
80 | "name": "String",
81 | "ofType": null
82 | }
83 | },
84 | "isDeprecated": false,
85 | "deprecationReason": null
86 | },
87 | {
88 | "name": "type",
89 | "description": "",
90 | "args": [],
91 | "type": {
92 | "kind": "NON_NULL",
93 | "name": null,
94 | "ofType": {
95 | "kind": "SCALAR",
96 | "name": "String",
97 | "ofType": null
98 | }
99 | },
100 | "isDeprecated": false,
101 | "deprecationReason": null
102 | }
103 | ],
104 | "inputFields": null,
105 | "interfaces": [],
106 | "enumValues": null,
107 | "possibleTypes": null
108 | },
109 | {
110 | "kind": "SCALAR",
111 | "name": "String",
112 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
113 | "fields": null,
114 | "inputFields": null,
115 | "interfaces": null,
116 | "enumValues": null,
117 | "possibleTypes": null
118 | },
119 | {
120 | "kind": "OBJECT",
121 | "name": "Mutation",
122 | "description": "",
123 | "fields": [
124 | {
125 | "name": "addTodo",
126 | "description": "",
127 | "args": [
128 | {
129 | "name": "type",
130 | "description": "",
131 | "type": {
132 | "kind": "NON_NULL",
133 | "name": null,
134 | "ofType": {
135 | "kind": "SCALAR",
136 | "name": "String",
137 | "ofType": null
138 | }
139 | },
140 | "defaultValue": null
141 | }
142 | ],
143 | "type": {
144 | "kind": "OBJECT",
145 | "name": "Todo",
146 | "ofType": null
147 | },
148 | "isDeprecated": false,
149 | "deprecationReason": null
150 | },
151 | {
152 | "name": "updateTodo",
153 | "description": "",
154 | "args": [
155 | {
156 | "name": "id",
157 | "description": "",
158 | "type": {
159 | "kind": "NON_NULL",
160 | "name": null,
161 | "ofType": {
162 | "kind": "SCALAR",
163 | "name": "String",
164 | "ofType": null
165 | }
166 | },
167 | "defaultValue": null
168 | },
169 | {
170 | "name": "type",
171 | "description": "",
172 | "type": {
173 | "kind": "NON_NULL",
174 | "name": null,
175 | "ofType": {
176 | "kind": "SCALAR",
177 | "name": "String",
178 | "ofType": null
179 | }
180 | },
181 | "defaultValue": null
182 | }
183 | ],
184 | "type": {
185 | "kind": "OBJECT",
186 | "name": "Todo",
187 | "ofType": null
188 | },
189 | "isDeprecated": false,
190 | "deprecationReason": null
191 | }
192 | ],
193 | "inputFields": null,
194 | "interfaces": [],
195 | "enumValues": null,
196 | "possibleTypes": null
197 | },
198 | {
199 | "kind": "OBJECT",
200 | "name": "__Schema",
201 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
202 | "fields": [
203 | {
204 | "name": "types",
205 | "description": "A list of all types supported by this server.",
206 | "args": [],
207 | "type": {
208 | "kind": "NON_NULL",
209 | "name": null,
210 | "ofType": {
211 | "kind": "LIST",
212 | "name": null,
213 | "ofType": {
214 | "kind": "NON_NULL",
215 | "name": null,
216 | "ofType": {
217 | "kind": "OBJECT",
218 | "name": "__Type",
219 | "ofType": null
220 | }
221 | }
222 | }
223 | },
224 | "isDeprecated": false,
225 | "deprecationReason": null
226 | },
227 | {
228 | "name": "queryType",
229 | "description": "The type that query operations will be rooted at.",
230 | "args": [],
231 | "type": {
232 | "kind": "NON_NULL",
233 | "name": null,
234 | "ofType": {
235 | "kind": "OBJECT",
236 | "name": "__Type",
237 | "ofType": null
238 | }
239 | },
240 | "isDeprecated": false,
241 | "deprecationReason": null
242 | },
243 | {
244 | "name": "mutationType",
245 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
246 | "args": [],
247 | "type": {
248 | "kind": "OBJECT",
249 | "name": "__Type",
250 | "ofType": null
251 | },
252 | "isDeprecated": false,
253 | "deprecationReason": null
254 | },
255 | {
256 | "name": "subscriptionType",
257 | "description": "If this server support subscription, the type that subscription operations will be rooted at.",
258 | "args": [],
259 | "type": {
260 | "kind": "OBJECT",
261 | "name": "__Type",
262 | "ofType": null
263 | },
264 | "isDeprecated": false,
265 | "deprecationReason": null
266 | },
267 | {
268 | "name": "directives",
269 | "description": "A list of all directives supported by this server.",
270 | "args": [],
271 | "type": {
272 | "kind": "NON_NULL",
273 | "name": null,
274 | "ofType": {
275 | "kind": "LIST",
276 | "name": null,
277 | "ofType": {
278 | "kind": "NON_NULL",
279 | "name": null,
280 | "ofType": {
281 | "kind": "OBJECT",
282 | "name": "__Directive",
283 | "ofType": null
284 | }
285 | }
286 | }
287 | },
288 | "isDeprecated": false,
289 | "deprecationReason": null
290 | }
291 | ],
292 | "inputFields": null,
293 | "interfaces": [],
294 | "enumValues": null,
295 | "possibleTypes": null
296 | },
297 | {
298 | "kind": "OBJECT",
299 | "name": "__Type",
300 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.",
301 | "fields": [
302 | {
303 | "name": "kind",
304 | "description": null,
305 | "args": [],
306 | "type": {
307 | "kind": "NON_NULL",
308 | "name": null,
309 | "ofType": {
310 | "kind": "ENUM",
311 | "name": "__TypeKind",
312 | "ofType": null
313 | }
314 | },
315 | "isDeprecated": false,
316 | "deprecationReason": null
317 | },
318 | {
319 | "name": "name",
320 | "description": null,
321 | "args": [],
322 | "type": {
323 | "kind": "SCALAR",
324 | "name": "String",
325 | "ofType": null
326 | },
327 | "isDeprecated": false,
328 | "deprecationReason": null
329 | },
330 | {
331 | "name": "description",
332 | "description": null,
333 | "args": [],
334 | "type": {
335 | "kind": "SCALAR",
336 | "name": "String",
337 | "ofType": null
338 | },
339 | "isDeprecated": false,
340 | "deprecationReason": null
341 | },
342 | {
343 | "name": "fields",
344 | "description": null,
345 | "args": [
346 | {
347 | "name": "includeDeprecated",
348 | "description": null,
349 | "type": {
350 | "kind": "SCALAR",
351 | "name": "Boolean",
352 | "ofType": null
353 | },
354 | "defaultValue": "false"
355 | }
356 | ],
357 | "type": {
358 | "kind": "LIST",
359 | "name": null,
360 | "ofType": {
361 | "kind": "NON_NULL",
362 | "name": null,
363 | "ofType": {
364 | "kind": "OBJECT",
365 | "name": "__Field",
366 | "ofType": null
367 | }
368 | }
369 | },
370 | "isDeprecated": false,
371 | "deprecationReason": null
372 | },
373 | {
374 | "name": "interfaces",
375 | "description": null,
376 | "args": [],
377 | "type": {
378 | "kind": "LIST",
379 | "name": null,
380 | "ofType": {
381 | "kind": "NON_NULL",
382 | "name": null,
383 | "ofType": {
384 | "kind": "OBJECT",
385 | "name": "__Type",
386 | "ofType": null
387 | }
388 | }
389 | },
390 | "isDeprecated": false,
391 | "deprecationReason": null
392 | },
393 | {
394 | "name": "possibleTypes",
395 | "description": null,
396 | "args": [],
397 | "type": {
398 | "kind": "LIST",
399 | "name": null,
400 | "ofType": {
401 | "kind": "NON_NULL",
402 | "name": null,
403 | "ofType": {
404 | "kind": "OBJECT",
405 | "name": "__Type",
406 | "ofType": null
407 | }
408 | }
409 | },
410 | "isDeprecated": false,
411 | "deprecationReason": null
412 | },
413 | {
414 | "name": "enumValues",
415 | "description": null,
416 | "args": [
417 | {
418 | "name": "includeDeprecated",
419 | "description": null,
420 | "type": {
421 | "kind": "SCALAR",
422 | "name": "Boolean",
423 | "ofType": null
424 | },
425 | "defaultValue": "false"
426 | }
427 | ],
428 | "type": {
429 | "kind": "LIST",
430 | "name": null,
431 | "ofType": {
432 | "kind": "NON_NULL",
433 | "name": null,
434 | "ofType": {
435 | "kind": "OBJECT",
436 | "name": "__EnumValue",
437 | "ofType": null
438 | }
439 | }
440 | },
441 | "isDeprecated": false,
442 | "deprecationReason": null
443 | },
444 | {
445 | "name": "inputFields",
446 | "description": null,
447 | "args": [],
448 | "type": {
449 | "kind": "LIST",
450 | "name": null,
451 | "ofType": {
452 | "kind": "NON_NULL",
453 | "name": null,
454 | "ofType": {
455 | "kind": "OBJECT",
456 | "name": "__InputValue",
457 | "ofType": null
458 | }
459 | }
460 | },
461 | "isDeprecated": false,
462 | "deprecationReason": null
463 | },
464 | {
465 | "name": "ofType",
466 | "description": null,
467 | "args": [],
468 | "type": {
469 | "kind": "OBJECT",
470 | "name": "__Type",
471 | "ofType": null
472 | },
473 | "isDeprecated": false,
474 | "deprecationReason": null
475 | }
476 | ],
477 | "inputFields": null,
478 | "interfaces": [],
479 | "enumValues": null,
480 | "possibleTypes": null
481 | },
482 | {
483 | "kind": "ENUM",
484 | "name": "__TypeKind",
485 | "description": "An enum describing what kind of type a given `__Type` is.",
486 | "fields": null,
487 | "inputFields": null,
488 | "interfaces": null,
489 | "enumValues": [
490 | {
491 | "name": "SCALAR",
492 | "description": "Indicates this type is a scalar.",
493 | "isDeprecated": false,
494 | "deprecationReason": null
495 | },
496 | {
497 | "name": "OBJECT",
498 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
499 | "isDeprecated": false,
500 | "deprecationReason": null
501 | },
502 | {
503 | "name": "INTERFACE",
504 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
505 | "isDeprecated": false,
506 | "deprecationReason": null
507 | },
508 | {
509 | "name": "UNION",
510 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
511 | "isDeprecated": false,
512 | "deprecationReason": null
513 | },
514 | {
515 | "name": "ENUM",
516 | "description": "Indicates this type is an enum. `enumValues` is a valid field.",
517 | "isDeprecated": false,
518 | "deprecationReason": null
519 | },
520 | {
521 | "name": "INPUT_OBJECT",
522 | "description": "Indicates this type is an input object. `inputFields` is a valid field.",
523 | "isDeprecated": false,
524 | "deprecationReason": null
525 | },
526 | {
527 | "name": "LIST",
528 | "description": "Indicates this type is a list. `ofType` is a valid field.",
529 | "isDeprecated": false,
530 | "deprecationReason": null
531 | },
532 | {
533 | "name": "NON_NULL",
534 | "description": "Indicates this type is a non-null. `ofType` is a valid field.",
535 | "isDeprecated": false,
536 | "deprecationReason": null
537 | }
538 | ],
539 | "possibleTypes": null
540 | },
541 | {
542 | "kind": "SCALAR",
543 | "name": "Boolean",
544 | "description": "The `Boolean` scalar type represents `true` or `false`.",
545 | "fields": null,
546 | "inputFields": null,
547 | "interfaces": null,
548 | "enumValues": null,
549 | "possibleTypes": null
550 | },
551 | {
552 | "kind": "OBJECT",
553 | "name": "__Field",
554 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
555 | "fields": [
556 | {
557 | "name": "name",
558 | "description": null,
559 | "args": [],
560 | "type": {
561 | "kind": "NON_NULL",
562 | "name": null,
563 | "ofType": {
564 | "kind": "SCALAR",
565 | "name": "String",
566 | "ofType": null
567 | }
568 | },
569 | "isDeprecated": false,
570 | "deprecationReason": null
571 | },
572 | {
573 | "name": "description",
574 | "description": null,
575 | "args": [],
576 | "type": {
577 | "kind": "SCALAR",
578 | "name": "String",
579 | "ofType": null
580 | },
581 | "isDeprecated": false,
582 | "deprecationReason": null
583 | },
584 | {
585 | "name": "args",
586 | "description": null,
587 | "args": [],
588 | "type": {
589 | "kind": "NON_NULL",
590 | "name": null,
591 | "ofType": {
592 | "kind": "LIST",
593 | "name": null,
594 | "ofType": {
595 | "kind": "NON_NULL",
596 | "name": null,
597 | "ofType": {
598 | "kind": "OBJECT",
599 | "name": "__InputValue",
600 | "ofType": null
601 | }
602 | }
603 | }
604 | },
605 | "isDeprecated": false,
606 | "deprecationReason": null
607 | },
608 | {
609 | "name": "type",
610 | "description": null,
611 | "args": [],
612 | "type": {
613 | "kind": "NON_NULL",
614 | "name": null,
615 | "ofType": {
616 | "kind": "OBJECT",
617 | "name": "__Type",
618 | "ofType": null
619 | }
620 | },
621 | "isDeprecated": false,
622 | "deprecationReason": null
623 | },
624 | {
625 | "name": "isDeprecated",
626 | "description": null,
627 | "args": [],
628 | "type": {
629 | "kind": "NON_NULL",
630 | "name": null,
631 | "ofType": {
632 | "kind": "SCALAR",
633 | "name": "Boolean",
634 | "ofType": null
635 | }
636 | },
637 | "isDeprecated": false,
638 | "deprecationReason": null
639 | },
640 | {
641 | "name": "deprecationReason",
642 | "description": null,
643 | "args": [],
644 | "type": {
645 | "kind": "SCALAR",
646 | "name": "String",
647 | "ofType": null
648 | },
649 | "isDeprecated": false,
650 | "deprecationReason": null
651 | }
652 | ],
653 | "inputFields": null,
654 | "interfaces": [],
655 | "enumValues": null,
656 | "possibleTypes": null
657 | },
658 | {
659 | "kind": "OBJECT",
660 | "name": "__InputValue",
661 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
662 | "fields": [
663 | {
664 | "name": "name",
665 | "description": null,
666 | "args": [],
667 | "type": {
668 | "kind": "NON_NULL",
669 | "name": null,
670 | "ofType": {
671 | "kind": "SCALAR",
672 | "name": "String",
673 | "ofType": null
674 | }
675 | },
676 | "isDeprecated": false,
677 | "deprecationReason": null
678 | },
679 | {
680 | "name": "description",
681 | "description": null,
682 | "args": [],
683 | "type": {
684 | "kind": "SCALAR",
685 | "name": "String",
686 | "ofType": null
687 | },
688 | "isDeprecated": false,
689 | "deprecationReason": null
690 | },
691 | {
692 | "name": "type",
693 | "description": null,
694 | "args": [],
695 | "type": {
696 | "kind": "NON_NULL",
697 | "name": null,
698 | "ofType": {
699 | "kind": "OBJECT",
700 | "name": "__Type",
701 | "ofType": null
702 | }
703 | },
704 | "isDeprecated": false,
705 | "deprecationReason": null
706 | },
707 | {
708 | "name": "defaultValue",
709 | "description": "A GraphQL-formatted string representing the default value for this input value.",
710 | "args": [],
711 | "type": {
712 | "kind": "SCALAR",
713 | "name": "String",
714 | "ofType": null
715 | },
716 | "isDeprecated": false,
717 | "deprecationReason": null
718 | }
719 | ],
720 | "inputFields": null,
721 | "interfaces": [],
722 | "enumValues": null,
723 | "possibleTypes": null
724 | },
725 | {
726 | "kind": "OBJECT",
727 | "name": "__EnumValue",
728 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
729 | "fields": [
730 | {
731 | "name": "name",
732 | "description": null,
733 | "args": [],
734 | "type": {
735 | "kind": "NON_NULL",
736 | "name": null,
737 | "ofType": {
738 | "kind": "SCALAR",
739 | "name": "String",
740 | "ofType": null
741 | }
742 | },
743 | "isDeprecated": false,
744 | "deprecationReason": null
745 | },
746 | {
747 | "name": "description",
748 | "description": null,
749 | "args": [],
750 | "type": {
751 | "kind": "SCALAR",
752 | "name": "String",
753 | "ofType": null
754 | },
755 | "isDeprecated": false,
756 | "deprecationReason": null
757 | },
758 | {
759 | "name": "isDeprecated",
760 | "description": null,
761 | "args": [],
762 | "type": {
763 | "kind": "NON_NULL",
764 | "name": null,
765 | "ofType": {
766 | "kind": "SCALAR",
767 | "name": "Boolean",
768 | "ofType": null
769 | }
770 | },
771 | "isDeprecated": false,
772 | "deprecationReason": null
773 | },
774 | {
775 | "name": "deprecationReason",
776 | "description": null,
777 | "args": [],
778 | "type": {
779 | "kind": "SCALAR",
780 | "name": "String",
781 | "ofType": null
782 | },
783 | "isDeprecated": false,
784 | "deprecationReason": null
785 | }
786 | ],
787 | "inputFields": null,
788 | "interfaces": [],
789 | "enumValues": null,
790 | "possibleTypes": null
791 | },
792 | {
793 | "kind": "OBJECT",
794 | "name": "__Directive",
795 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.",
796 | "fields": [
797 | {
798 | "name": "name",
799 | "description": null,
800 | "args": [],
801 | "type": {
802 | "kind": "NON_NULL",
803 | "name": null,
804 | "ofType": {
805 | "kind": "SCALAR",
806 | "name": "String",
807 | "ofType": null
808 | }
809 | },
810 | "isDeprecated": false,
811 | "deprecationReason": null
812 | },
813 | {
814 | "name": "description",
815 | "description": null,
816 | "args": [],
817 | "type": {
818 | "kind": "SCALAR",
819 | "name": "String",
820 | "ofType": null
821 | },
822 | "isDeprecated": false,
823 | "deprecationReason": null
824 | },
825 | {
826 | "name": "locations",
827 | "description": null,
828 | "args": [],
829 | "type": {
830 | "kind": "NON_NULL",
831 | "name": null,
832 | "ofType": {
833 | "kind": "LIST",
834 | "name": null,
835 | "ofType": {
836 | "kind": "NON_NULL",
837 | "name": null,
838 | "ofType": {
839 | "kind": "ENUM",
840 | "name": "__DirectiveLocation",
841 | "ofType": null
842 | }
843 | }
844 | }
845 | },
846 | "isDeprecated": false,
847 | "deprecationReason": null
848 | },
849 | {
850 | "name": "args",
851 | "description": null,
852 | "args": [],
853 | "type": {
854 | "kind": "NON_NULL",
855 | "name": null,
856 | "ofType": {
857 | "kind": "LIST",
858 | "name": null,
859 | "ofType": {
860 | "kind": "NON_NULL",
861 | "name": null,
862 | "ofType": {
863 | "kind": "OBJECT",
864 | "name": "__InputValue",
865 | "ofType": null
866 | }
867 | }
868 | }
869 | },
870 | "isDeprecated": false,
871 | "deprecationReason": null
872 | },
873 | {
874 | "name": "onOperation",
875 | "description": null,
876 | "args": [],
877 | "type": {
878 | "kind": "NON_NULL",
879 | "name": null,
880 | "ofType": {
881 | "kind": "SCALAR",
882 | "name": "Boolean",
883 | "ofType": null
884 | }
885 | },
886 | "isDeprecated": true,
887 | "deprecationReason": "Use `locations`."
888 | },
889 | {
890 | "name": "onFragment",
891 | "description": null,
892 | "args": [],
893 | "type": {
894 | "kind": "NON_NULL",
895 | "name": null,
896 | "ofType": {
897 | "kind": "SCALAR",
898 | "name": "Boolean",
899 | "ofType": null
900 | }
901 | },
902 | "isDeprecated": true,
903 | "deprecationReason": "Use `locations`."
904 | },
905 | {
906 | "name": "onField",
907 | "description": null,
908 | "args": [],
909 | "type": {
910 | "kind": "NON_NULL",
911 | "name": null,
912 | "ofType": {
913 | "kind": "SCALAR",
914 | "name": "Boolean",
915 | "ofType": null
916 | }
917 | },
918 | "isDeprecated": true,
919 | "deprecationReason": "Use `locations`."
920 | }
921 | ],
922 | "inputFields": null,
923 | "interfaces": [],
924 | "enumValues": null,
925 | "possibleTypes": null
926 | },
927 | {
928 | "kind": "ENUM",
929 | "name": "__DirectiveLocation",
930 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.",
931 | "fields": null,
932 | "inputFields": null,
933 | "interfaces": null,
934 | "enumValues": [
935 | {
936 | "name": "QUERY",
937 | "description": "Location adjacent to a query operation.",
938 | "isDeprecated": false,
939 | "deprecationReason": null
940 | },
941 | {
942 | "name": "MUTATION",
943 | "description": "Location adjacent to a mutation operation.",
944 | "isDeprecated": false,
945 | "deprecationReason": null
946 | },
947 | {
948 | "name": "SUBSCRIPTION",
949 | "description": "Location adjacent to a subscription operation.",
950 | "isDeprecated": false,
951 | "deprecationReason": null
952 | },
953 | {
954 | "name": "FIELD",
955 | "description": "Location adjacent to a field.",
956 | "isDeprecated": false,
957 | "deprecationReason": null
958 | },
959 | {
960 | "name": "FRAGMENT_DEFINITION",
961 | "description": "Location adjacent to a fragment definition.",
962 | "isDeprecated": false,
963 | "deprecationReason": null
964 | },
965 | {
966 | "name": "FRAGMENT_SPREAD",
967 | "description": "Location adjacent to a fragment spread.",
968 | "isDeprecated": false,
969 | "deprecationReason": null
970 | },
971 | {
972 | "name": "INLINE_FRAGMENT",
973 | "description": "Location adjacent to an inline fragment.",
974 | "isDeprecated": false,
975 | "deprecationReason": null
976 | },
977 | {
978 | "name": "SCHEMA",
979 | "description": "Location adjacent to a schema definition.",
980 | "isDeprecated": false,
981 | "deprecationReason": null
982 | },
983 | {
984 | "name": "SCALAR",
985 | "description": "Location adjacent to a scalar definition.",
986 | "isDeprecated": false,
987 | "deprecationReason": null
988 | },
989 | {
990 | "name": "OBJECT",
991 | "description": "Location adjacent to an object type definition.",
992 | "isDeprecated": false,
993 | "deprecationReason": null
994 | },
995 | {
996 | "name": "FIELD_DEFINITION",
997 | "description": "Location adjacent to a field definition.",
998 | "isDeprecated": false,
999 | "deprecationReason": null
1000 | },
1001 | {
1002 | "name": "ARGUMENT_DEFINITION",
1003 | "description": "Location adjacent to an argument definition.",
1004 | "isDeprecated": false,
1005 | "deprecationReason": null
1006 | },
1007 | {
1008 | "name": "INTERFACE",
1009 | "description": "Location adjacent to an interface definition.",
1010 | "isDeprecated": false,
1011 | "deprecationReason": null
1012 | },
1013 | {
1014 | "name": "UNION",
1015 | "description": "Location adjacent to a union definition.",
1016 | "isDeprecated": false,
1017 | "deprecationReason": null
1018 | },
1019 | {
1020 | "name": "ENUM",
1021 | "description": "Location adjacent to an enum definition.",
1022 | "isDeprecated": false,
1023 | "deprecationReason": null
1024 | },
1025 | {
1026 | "name": "ENUM_VALUE",
1027 | "description": "Location adjacent to an enum value definition.",
1028 | "isDeprecated": false,
1029 | "deprecationReason": null
1030 | },
1031 | {
1032 | "name": "INPUT_OBJECT",
1033 | "description": "Location adjacent to an input object type definition.",
1034 | "isDeprecated": false,
1035 | "deprecationReason": null
1036 | },
1037 | {
1038 | "name": "INPUT_FIELD_DEFINITION",
1039 | "description": "Location adjacent to an input object field definition.",
1040 | "isDeprecated": false,
1041 | "deprecationReason": null
1042 | }
1043 | ],
1044 | "possibleTypes": null
1045 | }
1046 | ],
1047 | "directives": [
1048 | {
1049 | "name": "skip",
1050 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
1051 | "locations": [
1052 | "FIELD",
1053 | "FRAGMENT_SPREAD",
1054 | "INLINE_FRAGMENT"
1055 | ],
1056 | "args": [
1057 | {
1058 | "name": "if",
1059 | "description": "Skipped when true.",
1060 | "type": {
1061 | "kind": "NON_NULL",
1062 | "name": null,
1063 | "ofType": {
1064 | "kind": "SCALAR",
1065 | "name": "Boolean",
1066 | "ofType": null
1067 | }
1068 | },
1069 | "defaultValue": null
1070 | }
1071 | ]
1072 | },
1073 | {
1074 | "name": "include",
1075 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.",
1076 | "locations": [
1077 | "FIELD",
1078 | "FRAGMENT_SPREAD",
1079 | "INLINE_FRAGMENT"
1080 | ],
1081 | "args": [
1082 | {
1083 | "name": "if",
1084 | "description": "Included when true.",
1085 | "type": {
1086 | "kind": "NON_NULL",
1087 | "name": null,
1088 | "ofType": {
1089 | "kind": "SCALAR",
1090 | "name": "Boolean",
1091 | "ofType": null
1092 | }
1093 | },
1094 | "defaultValue": null
1095 | }
1096 | ]
1097 | },
1098 | {
1099 | "name": "deprecated",
1100 | "description": "Marks an element of a GraphQL schema as no longer supported.",
1101 | "locations": [
1102 | "FIELD_DEFINITION",
1103 | "ENUM_VALUE"
1104 | ],
1105 | "args": [
1106 | {
1107 | "name": "reason",
1108 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).",
1109 | "type": {
1110 | "kind": "SCALAR",
1111 | "name": "String",
1112 | "ofType": null
1113 | },
1114 | "defaultValue": "\"No longer supported\""
1115 | }
1116 | ]
1117 | }
1118 | ]
1119 | }
1120 | },
1121 | "extensions": {
1122 | "tracing": {
1123 | "version": 1,
1124 | "startTime": "2018-03-24T22:29:59.753Z",
1125 | "endTime": "2018-03-24T22:29:59.794Z",
1126 | "duration": 41261309,
1127 | "execution": {
1128 | "resolvers": []
1129 | }
1130 | }
1131 | }
1132 | }
--------------------------------------------------------------------------------