├── .gitignore ├── js ├── queries │ └── StoreQueries.js ├── components │ ├── About.js │ ├── NoMatch.js │ ├── App.js │ ├── posts │ │ ├── Post.js │ │ ├── PostShow.js │ │ └── PostList.js │ └── people │ │ ├── PersonShow.js │ │ └── PersonList.js ├── mutations │ └── CreatePostMutation.js └── app.js ├── .babelrc ├── app.json ├── README.md ├── .tern-project ├── server ├── index.js ├── dev.js └── data │ ├── database.js │ ├── methods.js │ └── schema.js ├── scripts ├── seed.js └── updateSchema.js ├── webpack.config.js ├── public └── index.html ├── webpack.config.prod.js ├── LICENSE.txt ├── package.json └── data └── schema.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .DS_Store 4 | data/schema.graphql 5 | data/*.sqlite 6 | public/assets/* 7 | dist 8 | -------------------------------------------------------------------------------- /js/queries/StoreQueries.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay'; 2 | 3 | export default { 4 | store: () => Relay.QL`query { store }`, 5 | }; -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "env": { 4 | "development": { 5 | "presets": ["react-hmre"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /js/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => ( 4 |
Rendering About component
5 | ); 6 | 7 | export default About; -------------------------------------------------------------------------------- /js/components/NoMatch.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NoMatch = () => ( 4 |
Rendering NoMatch component
5 | ); 6 | 7 | export default NoMatch; -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps" : [{ 3 | "name" : "react-relay", 4 | "script" : "./server/dev.js", 5 | "watch" : "./server", 6 | "log_date_format" : "YYYY-MM-DD HH:mm Z", 7 | "exec_interpreter": "babel-node", 8 | "exec_mode": "fork" 9 | }] 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React starter kit with Graphql, Relay and Sequelize 2 | 3 | ``` 4 | git clone https://github.com/doabit/react-relay-graphql-starter-kit.git 5 | npm install 6 | ``` 7 | 8 | ## Development 9 | 10 | ``` 11 | npm run db:seed 12 | npm run update-schema 13 | npm run start-dev 14 | ``` 15 | 16 | ## Production 17 | 18 | ``` 19 | npm run build 20 | npm run start 21 | ``` 22 | 23 | open `http://localhost:3000` -------------------------------------------------------------------------------- /.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaVersion": 6, 3 | "libs": [], 4 | "loadEagerly": [ 5 | "js/**/*.js" 6 | ], 7 | "dontLoad": [ 8 | "node_modules/**/*.js" 9 | ], 10 | "plugins": { 11 | "complete_strings": {}, 12 | "node": {}, 13 | "lint": {}, 14 | "angular": {}, 15 | "requirejs": {}, 16 | "modules": {}, 17 | "es_modules": {}, 18 | "doc_comment": { 19 | "fullDocs": true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /js/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link} from 'react-router'; 3 | 4 | class App extends React.Component { 5 | render() { 6 | return ( 7 |
8 | 13 | {this.props.children} 14 |
15 | ) 16 | } 17 | } 18 | 19 | export default App; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import graphQLHTTP from 'express-graphql'; 3 | import Schema from './data/schema'; 4 | import { graphql } from 'graphql'; 5 | 6 | const APP_PORT = 3000; 7 | const app = express(); 8 | 9 | app.use(express.static('public')); 10 | 11 | app.use('/graphql', graphQLHTTP({ 12 | schema: Schema, 13 | graphiql: true 14 | })); 15 | 16 | app.listen(APP_PORT, (err) => { 17 | if(err) { 18 | throw err; 19 | } 20 | console.log(`App is now running on http://localhost:${APP_PORT}`); 21 | }); 22 | -------------------------------------------------------------------------------- /js/components/posts/Post.js: -------------------------------------------------------------------------------- 1 | import {Link} from 'react-router'; 2 | import React from 'react'; 3 | import Relay from 'react-relay'; 4 | 5 | class Post extends React.Component { 6 | render() { 7 | const {post} = this.props; 8 | return ( 9 |
  • 10 | {post.title} 11 |
  • 12 | ); 13 | } 14 | } 15 | 16 | export default Relay.createContainer(Post, { 17 | fragments: { 18 | post: () => Relay.QL` 19 | fragment on Post { 20 | id 21 | title 22 | } 23 | `, 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/seed.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import Faker from 'faker'; 3 | 4 | import { Conn, Post, Person } from '../server/data/database'; 5 | 6 | Conn.sync({force: true}).then(() => { 7 | _.times(10, (i) => { 8 | return Person.create({ 9 | firstName: Faker.name.firstName(), 10 | lastName: Faker.name.lastName(), 11 | email: Faker.internet.email() 12 | }).then(person => { 13 | return person.createPost({ 14 | title: `Sample title by ${person.firstName}`, 15 | content: 'this is content' 16 | }); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /js/components/people/PersonShow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Relay from 'react-relay'; 3 | 4 | class PersonShow extends React.Component { 5 | render() { 6 | const {person} = this.props; 7 | return ( 8 |
    9 |

    Person {person.firstName} {person.lastName}

    10 |

    Email: {person.email}

    11 |
    12 | ); 13 | } 14 | } 15 | 16 | export default Relay.createContainer(PersonShow, { 17 | fragments: { 18 | person: () => Relay.QL` 19 | fragment on Person { 20 | id 21 | firstName 22 | email 23 | } 24 | `, 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /js/components/posts/PostShow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Relay from 'react-relay'; 3 | 4 | class PostShow extends React.Component { 5 | render() { 6 | const {post} = this.props; 7 | return ( 8 |
    9 |

    post {post.id} {post.title}

    10 |

    Author: {post.person.firstName} {post.person.lastName}

    11 |

    {post.content}

    12 |
    13 | ); 14 | } 15 | } 16 | 17 | export default Relay.createContainer(PostShow, { 18 | fragments: { 19 | post: () => Relay.QL` 20 | fragment on Post { 21 | person{ 22 | firstName 23 | lastName 24 | } 25 | id 26 | title 27 | content 28 | } 29 | `, 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'cheap-module-eval-source-map', 6 | entry: [ 7 | 'webpack-hot-middleware/client', 8 | './js/app' 9 | ], 10 | output: { 11 | path: path.join(__dirname, 'public/assets'), 12 | filename: 'bundle.js', 13 | publicPath: '/assets/' 14 | }, 15 | plugins: [ 16 | new webpack.HotModuleReplacementPlugin(), 17 | new webpack.NoErrorsPlugin() 18 | ], 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.js$/, 23 | exclude: /node_modules/, 24 | include: path.join(__dirname, 'js'), 25 | loader: 'babel', 26 | query: {plugins: ['./build/babelRelayPlugin']}, 27 | } 28 | ] 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /server/dev.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import config from '../webpack.config'; 3 | import express from 'express'; 4 | import graphQLHTTP from 'express-graphql'; 5 | import Schema from './data/schema'; 6 | import { graphql } from 'graphql'; 7 | 8 | const APP_PORT = 3000; 9 | const app = express(); 10 | const compiler = webpack(config); 11 | 12 | app.use(require('webpack-dev-middleware')(compiler, { 13 | publicPath: config.output.publicPath, 14 | hot: true, 15 | contentBase: './public', 16 | historyApiFallback: true 17 | })); 18 | 19 | app.use(require('webpack-hot-middleware')(compiler)); 20 | 21 | app.use(express.static('public')); 22 | 23 | app.use('/graphql', graphQLHTTP({ 24 | schema: Schema, 25 | graphiql: true 26 | })); 27 | 28 | app.listen(APP_PORT, (err2) => { 29 | if(err2) { 30 | throw err2; 31 | } 32 | console.log(`App is now running on http://localhost:${APP_PORT}`); 33 | }); 34 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React relay graphql starter kit 7 | 8 | 9 |
    10 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | entry: [ 7 | './js/app' 8 | ], 9 | output: { 10 | path: path.join(__dirname, 'public/assets'), 11 | filename: 'bundle.js', 12 | publicPath: '/assets/' 13 | }, 14 | plugins: [ 15 | new webpack.optimize.OccurenceOrderPlugin(), 16 | new webpack.DefinePlugin({ 17 | 'process.env': { 18 | 'NODE_ENV': JSON.stringify('production') 19 | } 20 | }), 21 | new webpack.optimize.UglifyJsPlugin({ 22 | compressor: { 23 | warnings: false 24 | } 25 | }) 26 | ], 27 | module: { 28 | loaders: [ 29 | { 30 | test: /\.js$/, 31 | exclude: /node_modules/, 32 | include: path.join(__dirname, 'js'), 33 | loader: 'babel', 34 | query: {plugins: ['./build/babelRelayPlugin']}, 35 | } 36 | ] 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 doabit 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /server/data/database.js: -------------------------------------------------------------------------------- 1 | import Sequelize from 'sequelize'; 2 | import path from 'path'; 3 | 4 | const Conn = new Sequelize('development', null, null, { 5 | dialect: 'sqlite', 6 | storage: path.join(__dirname, '../../data/development.sqlite') 7 | }); 8 | 9 | const Person = Conn.define('person', { 10 | // type: { 11 | // type: new Sequelize.VIRTUAL(Sequelize.STRING), 12 | // get() { 13 | // return 'Person'; 14 | // } 15 | // }, 16 | firstName: { 17 | type: Sequelize.STRING, 18 | allowNull: false 19 | }, 20 | lastName: { 21 | type: Sequelize.STRING, 22 | allowNull: false 23 | }, 24 | email: { 25 | type: Sequelize.STRING, 26 | allowNull: false, 27 | validate: { 28 | isEmail: true 29 | } 30 | } 31 | }); 32 | 33 | const Post = Conn.define('post', { 34 | // type: { 35 | // type: new Sequelize.VIRTUAL(Sequelize.STRING), 36 | // get() { 37 | // return 'Post'; 38 | // } 39 | // }, 40 | title: { 41 | type: Sequelize.STRING, 42 | allowNull: false 43 | }, 44 | content: { 45 | type: Sequelize.STRING, 46 | allowNull: false 47 | } 48 | }); 49 | 50 | Person.hasMany(Post); 51 | Post.belongsTo(Person); 52 | 53 | module.exports = { 54 | Conn, Person, Post 55 | }; 56 | -------------------------------------------------------------------------------- /js/mutations/CreatePostMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from "react-relay"; 2 | 3 | class CreatePostMutation extends Relay.Mutation { 4 | getMutation() { 5 | return Relay.QL` 6 | mutation { createPost } 7 | `; 8 | } 9 | 10 | getVariables() { 11 | return { 12 | title: this.props.title, 13 | content: this.props.content, 14 | person: this.props.person 15 | } 16 | } 17 | 18 | getFatQuery() { 19 | return Relay.QL` 20 | fragment on CreatePostPayload { 21 | postEdge 22 | store { posts } 23 | } 24 | `; 25 | } 26 | 27 | getConfigs() { 28 | return [ 29 | // { 30 | // type: 'FIELDS_CHANGE', 31 | // fieldIDs: { 32 | // store: this.props.store.id 33 | // } 34 | // }, 35 | { 36 | type: 'RANGE_ADD', 37 | parentName: 'store', 38 | parentID: this.props.store.id, 39 | connectionName: 'posts', 40 | edgeName: 'postEdge', 41 | rangeBehaviors: { 42 | '': 'prepend', 43 | }, 44 | }] 45 | } 46 | 47 | // getOptimisticResponse() { 48 | // return { 49 | // postEdge: { 50 | // node: { 51 | // title: this.props.title, 52 | // content: this.props.content, 53 | // } 54 | // }, 55 | // store: { 56 | // id: this.props.store.id 57 | // }, 58 | // } 59 | // } 60 | } 61 | 62 | export default CreatePostMutation; 63 | -------------------------------------------------------------------------------- /js/components/people/PersonList.js: -------------------------------------------------------------------------------- 1 | import {IndexLink, Link} from 'react-router'; 2 | import React from 'react'; 3 | import Relay from 'react-relay'; 4 | 5 | 6 | class PersonList extends React.Component { 7 | render() { 8 | var currentNumber = this.props.relay.variables.limit; 9 | var buttonStyle = {}; 10 | if (!this.props.store.people.pageInfo.hasNextPage) { 11 | buttonStyle.display = 'none'; 12 | } 13 | 14 | return ( 15 |
    16 |

    People list

    17 | 24 | 29 |
    30 | ); 31 | } 32 | } 33 | 34 | export default Relay.createContainer(PersonList, { 35 | initialVariables: { 36 | limit: 5 37 | }, 38 | fragments: { 39 | store: () => Relay.QL` 40 | fragment on Store { 41 | people(first: $limit){ 42 | edges{ 43 | node{ 44 | id 45 | firstName 46 | lastName 47 | email 48 | } 49 | }, 50 | pageInfo { 51 | hasNextPage 52 | } 53 | } 54 | } 55 | `, 56 | }, 57 | }); 58 | -------------------------------------------------------------------------------- /scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node --optional es7.asyncFunctions 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import fs from 'fs'; 15 | import path from 'path'; 16 | import Schema from '../server/data/schema'; 17 | import { graphql } from 'graphql'; 18 | import { introspectionQuery, printSchema } from 'graphql/utilities'; 19 | 20 | // Save JSON of full schema introspection for Babel Relay Plugin to use 21 | (async () => { 22 | var result = await (graphql(Schema, introspectionQuery)); 23 | if (result.errors) { 24 | console.error( 25 | 'ERROR introspecting schema: ', 26 | JSON.stringify(result.errors, null, 2) 27 | ); 28 | } else { 29 | fs.writeFileSync( 30 | path.join(__dirname, '../data/schema.json'), 31 | JSON.stringify(result, null, 2) 32 | ); 33 | } 34 | })(); 35 | 36 | 37 | // Save user readable type system shorthand of schema 38 | fs.writeFileSync( 39 | path.join(__dirname, '../data/schema.graphql'), 40 | printSchema(Schema) 41 | ); 42 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | // import 'babel-core/polyfill'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import Relay from 'react-relay'; 5 | 6 | import {createHashHistory} from 'history'; 7 | import {IndexRoute, Route} from 'react-router'; 8 | import {RelayRouter} from 'react-router-relay'; 9 | 10 | import App from './components/App'; 11 | import About from './components/About'; 12 | import PersonList from './components/people/PersonList'; 13 | import PersonShow from './components/people/PersonShow'; 14 | import PostList from './components/posts/PostList'; 15 | import PostShow from './components/posts/PostShow'; 16 | 17 | import NoMatch from './components/NoMatch'; 18 | 19 | import StoreQueries from './queries/StoreQueries'; 20 | 21 | const PersonQueries = { 22 | person: () => Relay.QL`query { node(id: $id) }` 23 | }; 24 | 25 | const PostQueries = { 26 | post: () => Relay.QL`query { node(id: $id) }` 27 | }; 28 | 29 | ReactDOM.render( 30 | 31 | 33 | 37 | 42 | 47 | 52 | 57 | 58 | 59 | 60 | , 61 | document.getElementById('root') 62 | ); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-relay-graphql-starter-kit", 3 | "version": "1.0.0", 4 | "description": "React starter kit with relay, graphql and sequelize", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/doabit/react-relay-graphql-starter-kit.git" 8 | }, 9 | "main": "server.js", 10 | "scripts": { 11 | "clean": "rm -rf dist public/assets && mkdir dist", 12 | "start": "node ./dist/index.js", 13 | "start-dev": "babel-node ./server/dev.js", 14 | "build:webpack": "NODE_ENV=production webpack --config webpack.config.prod.js --progress --colors", 15 | "build:server": "babel -d ./dist ./server", 16 | "build": "npm run clean && npm run build:webpack && npm run build:server", 17 | "update-schema": "babel-node ./scripts/updateSchema.js", 18 | "db:seed": "babel-node ./scripts/seed.js" 19 | }, 20 | "keywords": [ 21 | "React", 22 | "Graphql", 23 | "Relay", 24 | "Sequelize", 25 | "Babel 6" 26 | ], 27 | "author": "doabit", 28 | "license": "MIT", 29 | "dependencies": { 30 | "express": "^4.13.3", 31 | "express-graphql": "^0.4.5", 32 | "graphql": "^0.4.14", 33 | "graphql-relay": "^0.3.5", 34 | "history": "^1.13.1", 35 | "lodash": "^3.10.1", 36 | "react": "^0.14.3", 37 | "react-dom": "^0.14.3", 38 | "react-relay": "^0.6.1", 39 | "react-router": "^1.0.2", 40 | "react-router-relay": "^0.8.0", 41 | "sequelize": "^3.14.2", 42 | "faker": "^3.0.1", 43 | "sqlite3": "^3.1.1" 44 | }, 45 | "devDependencies": { 46 | "babel-core": "^6.3.17", 47 | "babel-loader": "^6.2.0", 48 | "babel-preset-es2015": "^6.3.13", 49 | "babel-preset-react": "^6.3.13", 50 | "babel-preset-stage-0": "^6.3.13", 51 | "babel-relay-plugin": "^0.6.3", 52 | "babel-preset-react-hmre": "^1.0.1", 53 | "webpack": "^1.12.9", 54 | "webpack-dev-middleware": "^1.4.0", 55 | "webpack-hot-middleware": "^2.6.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /server/data/methods.js: -------------------------------------------------------------------------------- 1 | // From https://github.com/MattMcFarland/sequelize-relay/blob/master/src/data/methods.js 2 | 3 | /* @flow */ 4 | 5 | type SequelizeClass = { 6 | findAll: Function 7 | } 8 | 9 | type Attributes = Object; 10 | type SequelizeModel = { 11 | type: String, 12 | dataValues: Attributes 13 | } 14 | 15 | /** 16 | * Converts an array of instances to an array of 17 | * objects. 18 | * @param instances 19 | * @param withMethods {Boolean} false by default. 20 | * @returns {Array.} 21 | */ 22 | export function getArrayData( 23 | instances: Array, 24 | withMethods: boolean = false 25 | ): Array { 26 | 27 | if (withMethods) { 28 | return [].concat(...instances); 29 | } else { 30 | return [].concat(instances.map(model => { 31 | return Object.assign({}, { 32 | type: model.type 33 | }, { 34 | ...model.dataValues 35 | }); 36 | })); 37 | } 38 | } 39 | 40 | 41 | /** 42 | * Returns an `Array` of 43 | * instances that are of the passed-in `Class`. 44 | * @param SeqClass 45 | * @returns {Array.} 46 | */ 47 | export function getModelsByClass( 48 | SeqClass: SequelizeClass 49 | ): Array { 50 | return SeqClass.findAll(); 51 | } 52 | 53 | 54 | 55 | /** 56 | * First, it internally resolves an an `Array` of 57 | * instances that are of the passed-in `Class`. 58 | * Then it converts the array into a **promised** `Array` of 59 | * objects. 60 | * @param SeqClass 61 | * @param withMethods {Boolean} false by default. 62 | * @returns {Array.} 63 | */ 64 | export function resolveArrayByClass( 65 | SeqClass: SequelizeClass, 66 | withMethods: boolean = false 67 | ): Promise> { 68 | return new Promise((resolve, reject) => { 69 | resolveModelsByClass(SeqClass).then(m => { 70 | resolve(getArrayData(m, withMethods)); 71 | }).catch(reject); 72 | }); 73 | 74 | } 75 | 76 | 77 | 78 | /** 79 | * Converts a promised `Array` of instances into a 80 | * **promised** `Array` of objects. 81 | * @param instances 82 | * @param withMethods {Boolean} false by default. 83 | * @returns {Promise>} 84 | */ 85 | export function resolveArrayData( 86 | instances: Promise>, 87 | withMethods: boolean = false 88 | ): Promise> { 89 | return new Promise((resolve, reject) => { 90 | instances.then((models) => { 91 | resolve(getArrayData(models, withMethods)); 92 | }).catch(reject); 93 | }); 94 | } 95 | 96 | 97 | /** 98 | * Returns a **promised** `Array` of objects by `Class`. 99 | * 100 | * @param SeqClass 101 | * @returns {Promise>} 102 | */ 103 | export function resolveModelsByClass( 104 | SeqClass: SequelizeClass 105 | ): Promise> { 106 | return SeqClass.findAll(); 107 | } -------------------------------------------------------------------------------- /js/components/posts/PostList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Relay from 'react-relay'; 3 | import Post from './Post'; 4 | import CreatePostMutation from "../../mutations/CreatePostMutation"; 5 | 6 | class PostList extends React.Component { 7 | // constructor(props) { 8 | // super(props); 9 | // } 10 | handleSubmit = (e) => { 11 | e.preventDefault(); 12 | let onSuccess = () => { 13 | console.log("Create post success"); 14 | }; 15 | let onFailure = (transaction) => { 16 | var error = transaction.getError() || new Error('Mutation failed.'); 17 | console.error(error); 18 | }; 19 | 20 | Relay.Store.commitUpdate( 21 | new CreatePostMutation({ 22 | title: this.refs.newTitle.value, 23 | content: this.refs.newContent.value, 24 | person: this.refs.newPerson.value, 25 | store: this.props.store 26 | }), 27 | {onFailure, onSuccess} 28 | ); 29 | this.refs.newTitle.value = ""; 30 | this.refs.newContent.value = ""; 31 | }; 32 | 33 | render() { 34 | let currentNumber = this.props.relay.variables.first; 35 | var buttonStyle = {}; 36 | if (!this.props.store.posts.pageInfo.hasNextPage) { 37 | buttonStyle.display = 'none'; 38 | } 39 | 40 | return ( 41 |
    42 |

    Post List

    43 |
      44 | {this.props.store.posts.edges.map(edge => 45 | 46 | )} 47 |
    48 | 56 | 57 |
    58 |
    59 |
    60 |
    Add New Post
    61 |

    62 |
    63 | 68 |

    69 |

    70 |
    71 | 72 |

    73 |

    74 |
    75 | 76 |

    77 |
    78 |
    79 | 82 | Cancel 83 |
    84 |
    85 |
    86 | 87 |
    88 | ); 89 | } 90 | } 91 | 92 | export default Relay.createContainer(PostList, { 93 | initialVariables: { 94 | first: 5 95 | }, 96 | fragments: { 97 | store: () => Relay.QL` 98 | fragment on Store { 99 | id 100 | people(first: 10){ 101 | edges{ 102 | node{ 103 | id 104 | firstName 105 | lastName 106 | email 107 | } 108 | } 109 | } 110 | 111 | posts(first: $first){ 112 | edges{ 113 | node{ 114 | id 115 | ${Post.getFragment('post')} 116 | } 117 | }, 118 | pageInfo { 119 | hasNextPage 120 | } 121 | } 122 | } 123 | `, 124 | }, 125 | }); 126 | -------------------------------------------------------------------------------- /server/data/schema.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType, 3 | GraphQLInt, 4 | GraphQLString, 5 | GraphQLList, 6 | GraphQLSchema, 7 | GraphQLNonNull 8 | } from 'graphql'; 9 | 10 | import { 11 | Post, 12 | Person 13 | } from './database'; 14 | 15 | import { 16 | fromGlobalId, 17 | globalIdField, 18 | nodeDefinitions, 19 | connectionArgs, 20 | connectionDefinitions, 21 | offsetToCursor, 22 | cursorForObjectInConnection, 23 | // connectionFromArray, 24 | connectionFromPromisedArray, 25 | mutationWithClientMutationId 26 | } from 'graphql-relay'; 27 | 28 | 29 | import { 30 | getArrayData, 31 | getModelsByClass, 32 | resolveArrayByClass, 33 | resolveArrayData, 34 | resolveModelsByClass 35 | } from './methods'; 36 | 37 | const _ = require('lodash'); 38 | 39 | var {nodeInterface, nodeField} = nodeDefinitions( 40 | (globalId) => { 41 | var {type, id} = fromGlobalId(globalId); 42 | switch (type) { 43 | case 'Person': 44 | return Person.findById(id); 45 | case 'Post': 46 | return Post.findById(id); 47 | case 'Store': 48 | return store; 49 | default: 50 | return null; 51 | } 52 | }, 53 | (obj) => { 54 | if (obj instanceof Person.Instance) { 55 | return PersonType; 56 | } else if (obj instanceof Post.Instance) { 57 | return PostType; 58 | } else if (obj instanceof Store) { 59 | return StoreType; 60 | } else { 61 | return null; 62 | } 63 | } 64 | ); 65 | 66 | const PersonType = new GraphQLObjectType({ 67 | name: 'Person', 68 | description: 'This response a Person', 69 | interfaces: [nodeInterface], 70 | fields: () => { 71 | return { 72 | id: globalIdField('Person'), 73 | firstName: { 74 | type: GraphQLString, 75 | resolve(person) { 76 | return person.firstName 77 | } 78 | }, 79 | lastName: { 80 | type: GraphQLString, 81 | resolve(person) { 82 | return person.lastName 83 | } 84 | }, 85 | email: { 86 | type: GraphQLString, 87 | resolve(person) { 88 | return person.email 89 | } 90 | }, 91 | posts: { 92 | type: postConnection, 93 | args: connectionArgs, 94 | resolve: (person, args) => { 95 | return connectionFromPromisedArray( 96 | resolveArrayData(person.getPosts(), true), args 97 | ) 98 | } 99 | } 100 | // posts: { 101 | // type: new GraphQLList(PostType), 102 | // resolve(person) { 103 | // console.log(person) 104 | // return person.getPosts() 105 | // } 106 | // } 107 | }; 108 | } 109 | }); 110 | 111 | 112 | const PostType = new GraphQLObjectType({ 113 | name: 'Post', 114 | description: 'This response Post', 115 | interfaces: [nodeInterface], 116 | fields: () => { 117 | return { 118 | id: globalIdField('Post'), 119 | title: { 120 | type: GraphQLString, 121 | resolve(post) { 122 | return post.title 123 | } 124 | }, 125 | content: { 126 | type: GraphQLString, 127 | resolve(post) { 128 | return post.content 129 | } 130 | }, 131 | person: { 132 | type: PersonType, 133 | resolve(post) { 134 | // return Person.findById(post.personId) 135 | return post.getPerson(); 136 | } 137 | } 138 | }; 139 | } 140 | }); 141 | 142 | const { 143 | connectionType: postConnection, 144 | edgeType: postEdge 145 | } = connectionDefinitions({name: 'Post', nodeType: PostType}); 146 | 147 | const { 148 | connectionType: personConnection, 149 | edgeType: personEdge 150 | } = connectionDefinitions({name: 'Person', nodeType: PersonType}); 151 | 152 | const StoreType = new GraphQLObjectType({ 153 | name: 'Store', 154 | interfaces: [nodeInterface], 155 | fields: () => ({ 156 | id: globalIdField("Store"), 157 | people: { 158 | type: personConnection, 159 | args: connectionArgs, 160 | resolve: (root, args) => { 161 | return connectionFromPromisedArray( 162 | resolveArrayData(Person.findAll(), true), args 163 | ) 164 | } 165 | }, 166 | posts: { 167 | type: postConnection, 168 | args: connectionArgs, 169 | resolve: (_, args) => { 170 | return connectionFromPromisedArray( 171 | resolveArrayData(Post.findAll(), true), args 172 | ) 173 | } 174 | } 175 | }), 176 | }); 177 | 178 | class Store {} 179 | // Mock data 180 | let store = new Store(); 181 | 182 | const Query = new GraphQLObjectType({ 183 | name: 'Query', 184 | fields: { 185 | node: nodeField, 186 | store: { 187 | type: StoreType, 188 | resolve: () => store, 189 | }, 190 | }, 191 | }); 192 | 193 | 194 | const createPostMutation = mutationWithClientMutationId({ 195 | name: 'CreatePost', 196 | 197 | inputFields: { 198 | title: { type: new GraphQLNonNull(GraphQLString) }, 199 | content: { type: new GraphQLNonNull(GraphQLString) }, 200 | person: { type: new GraphQLNonNull(GraphQLString) }, 201 | }, 202 | 203 | // outputFields: { 204 | // postEdge: { 205 | // type: postEdge, 206 | // resolve: ({obj}) => { 207 | // console.log("obj") 208 | // 209 | // // return { node: obj.ops[0], cursor: obj.insertedId } 210 | // } 211 | // }, 212 | // store: { 213 | // type: StoreType, 214 | // resolve: () => store 215 | // } 216 | // }, 217 | 218 | outputFields: { 219 | postEdge: { 220 | type: postEdge, 221 | resolve: ({newPost, cursor}) => { 222 | return { 223 | cursor, 224 | node: newPost 225 | } 226 | // return Post.findAll() 227 | // .then(posts => { 228 | // return { 229 | // cursor, 230 | // node: newPost 231 | // } 232 | // }) 233 | } 234 | }, 235 | store: { 236 | type: StoreType, 237 | resolve: () => store 238 | } 239 | }, 240 | 241 | mutateAndGetPayload: ({title, content, person}) => { 242 | const {type, id} = fromGlobalId(person); 243 | return Post.create({title: title, content: content, personId: id}) 244 | .then(newPost => { 245 | return Post.findAll().then(posts => { 246 | return { 247 | newPost, 248 | cursor: offsetToCursor( 249 | _.findIndex(posts, post => post.id === newPost.id) 250 | ) 251 | } 252 | }) 253 | }) 254 | } 255 | 256 | // mutateAndGetPayload: ({title, content}) => { 257 | // console.log(title); 258 | // Post.create({ 259 | // personId: 1, 260 | // title: title, 261 | // content: content 262 | // }).then( post =>{ 263 | // console.log(post.id); 264 | // return {post}; 265 | // }); 266 | // let localPostId = post.id 267 | // return {localPostId} 268 | // return db.collection("links").insertOne({ 269 | // title, 270 | // content, 271 | // createdAt: Date.now() 272 | // }); 273 | // } 274 | }); 275 | 276 | const Schema = new GraphQLSchema({ 277 | query: Query, 278 | mutation: new GraphQLObjectType({ 279 | name: 'Mutation', 280 | fields: () => ({ 281 | createPost: createPostMutation 282 | }) 283 | }) 284 | }); 285 | 286 | export default Schema; 287 | -------------------------------------------------------------------------------- /data/schema.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": null, 16 | "fields": [ 17 | { 18 | "name": "node", 19 | "description": "Fetches an object given its ID", 20 | "args": [ 21 | { 22 | "name": "id", 23 | "description": "The ID of an object", 24 | "type": { 25 | "kind": "NON_NULL", 26 | "name": null, 27 | "ofType": { 28 | "kind": "SCALAR", 29 | "name": "ID", 30 | "ofType": null 31 | } 32 | }, 33 | "defaultValue": null 34 | } 35 | ], 36 | "type": { 37 | "kind": "INTERFACE", 38 | "name": "Node", 39 | "ofType": null 40 | }, 41 | "isDeprecated": false, 42 | "deprecationReason": null 43 | }, 44 | { 45 | "name": "store", 46 | "description": null, 47 | "args": [], 48 | "type": { 49 | "kind": "OBJECT", 50 | "name": "Store", 51 | "ofType": null 52 | }, 53 | "isDeprecated": false, 54 | "deprecationReason": null 55 | } 56 | ], 57 | "inputFields": null, 58 | "interfaces": [], 59 | "enumValues": null, 60 | "possibleTypes": null 61 | }, 62 | { 63 | "kind": "SCALAR", 64 | "name": "ID", 65 | "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", 66 | "fields": null, 67 | "inputFields": null, 68 | "interfaces": null, 69 | "enumValues": null, 70 | "possibleTypes": null 71 | }, 72 | { 73 | "kind": "INTERFACE", 74 | "name": "Node", 75 | "description": "An object with an ID", 76 | "fields": [ 77 | { 78 | "name": "id", 79 | "description": "The id of the object.", 80 | "args": [], 81 | "type": { 82 | "kind": "NON_NULL", 83 | "name": null, 84 | "ofType": { 85 | "kind": "SCALAR", 86 | "name": "ID", 87 | "ofType": null 88 | } 89 | }, 90 | "isDeprecated": false, 91 | "deprecationReason": null 92 | } 93 | ], 94 | "inputFields": null, 95 | "interfaces": null, 96 | "enumValues": null, 97 | "possibleTypes": [ 98 | { 99 | "kind": "OBJECT", 100 | "name": "Person", 101 | "ofType": null 102 | }, 103 | { 104 | "kind": "OBJECT", 105 | "name": "Post", 106 | "ofType": null 107 | }, 108 | { 109 | "kind": "OBJECT", 110 | "name": "Store", 111 | "ofType": null 112 | } 113 | ] 114 | }, 115 | { 116 | "kind": "OBJECT", 117 | "name": "Person", 118 | "description": "This response a Person", 119 | "fields": [ 120 | { 121 | "name": "id", 122 | "description": "The ID of an object", 123 | "args": [], 124 | "type": { 125 | "kind": "NON_NULL", 126 | "name": null, 127 | "ofType": { 128 | "kind": "SCALAR", 129 | "name": "ID", 130 | "ofType": null 131 | } 132 | }, 133 | "isDeprecated": false, 134 | "deprecationReason": null 135 | }, 136 | { 137 | "name": "firstName", 138 | "description": null, 139 | "args": [], 140 | "type": { 141 | "kind": "SCALAR", 142 | "name": "String", 143 | "ofType": null 144 | }, 145 | "isDeprecated": false, 146 | "deprecationReason": null 147 | }, 148 | { 149 | "name": "lastName", 150 | "description": null, 151 | "args": [], 152 | "type": { 153 | "kind": "SCALAR", 154 | "name": "String", 155 | "ofType": null 156 | }, 157 | "isDeprecated": false, 158 | "deprecationReason": null 159 | }, 160 | { 161 | "name": "email", 162 | "description": null, 163 | "args": [], 164 | "type": { 165 | "kind": "SCALAR", 166 | "name": "String", 167 | "ofType": null 168 | }, 169 | "isDeprecated": false, 170 | "deprecationReason": null 171 | }, 172 | { 173 | "name": "posts", 174 | "description": null, 175 | "args": [ 176 | { 177 | "name": "after", 178 | "description": null, 179 | "type": { 180 | "kind": "SCALAR", 181 | "name": "String", 182 | "ofType": null 183 | }, 184 | "defaultValue": null 185 | }, 186 | { 187 | "name": "first", 188 | "description": null, 189 | "type": { 190 | "kind": "SCALAR", 191 | "name": "Int", 192 | "ofType": null 193 | }, 194 | "defaultValue": null 195 | }, 196 | { 197 | "name": "before", 198 | "description": null, 199 | "type": { 200 | "kind": "SCALAR", 201 | "name": "String", 202 | "ofType": null 203 | }, 204 | "defaultValue": null 205 | }, 206 | { 207 | "name": "last", 208 | "description": null, 209 | "type": { 210 | "kind": "SCALAR", 211 | "name": "Int", 212 | "ofType": null 213 | }, 214 | "defaultValue": null 215 | } 216 | ], 217 | "type": { 218 | "kind": "OBJECT", 219 | "name": "PostConnection", 220 | "ofType": null 221 | }, 222 | "isDeprecated": false, 223 | "deprecationReason": null 224 | } 225 | ], 226 | "inputFields": null, 227 | "interfaces": [ 228 | { 229 | "kind": "INTERFACE", 230 | "name": "Node", 231 | "ofType": null 232 | } 233 | ], 234 | "enumValues": null, 235 | "possibleTypes": null 236 | }, 237 | { 238 | "kind": "SCALAR", 239 | "name": "String", 240 | "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.", 241 | "fields": null, 242 | "inputFields": null, 243 | "interfaces": null, 244 | "enumValues": null, 245 | "possibleTypes": null 246 | }, 247 | { 248 | "kind": "SCALAR", 249 | "name": "Int", 250 | "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since represented in JSON as double-precision floating point numbers specifiedby [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).", 251 | "fields": null, 252 | "inputFields": null, 253 | "interfaces": null, 254 | "enumValues": null, 255 | "possibleTypes": null 256 | }, 257 | { 258 | "kind": "OBJECT", 259 | "name": "PostConnection", 260 | "description": "A connection to a list of items.", 261 | "fields": [ 262 | { 263 | "name": "pageInfo", 264 | "description": "Information to aid in pagination.", 265 | "args": [], 266 | "type": { 267 | "kind": "NON_NULL", 268 | "name": null, 269 | "ofType": { 270 | "kind": "OBJECT", 271 | "name": "PageInfo", 272 | "ofType": null 273 | } 274 | }, 275 | "isDeprecated": false, 276 | "deprecationReason": null 277 | }, 278 | { 279 | "name": "edges", 280 | "description": "Information to aid in pagination.", 281 | "args": [], 282 | "type": { 283 | "kind": "LIST", 284 | "name": null, 285 | "ofType": { 286 | "kind": "OBJECT", 287 | "name": "PostEdge", 288 | "ofType": null 289 | } 290 | }, 291 | "isDeprecated": false, 292 | "deprecationReason": null 293 | } 294 | ], 295 | "inputFields": null, 296 | "interfaces": [], 297 | "enumValues": null, 298 | "possibleTypes": null 299 | }, 300 | { 301 | "kind": "OBJECT", 302 | "name": "PageInfo", 303 | "description": "Information about pagination in a connection.", 304 | "fields": [ 305 | { 306 | "name": "hasNextPage", 307 | "description": "When paginating forwards, are there more items?", 308 | "args": [], 309 | "type": { 310 | "kind": "NON_NULL", 311 | "name": null, 312 | "ofType": { 313 | "kind": "SCALAR", 314 | "name": "Boolean", 315 | "ofType": null 316 | } 317 | }, 318 | "isDeprecated": false, 319 | "deprecationReason": null 320 | }, 321 | { 322 | "name": "hasPreviousPage", 323 | "description": "When paginating backwards, are there more items?", 324 | "args": [], 325 | "type": { 326 | "kind": "NON_NULL", 327 | "name": null, 328 | "ofType": { 329 | "kind": "SCALAR", 330 | "name": "Boolean", 331 | "ofType": null 332 | } 333 | }, 334 | "isDeprecated": false, 335 | "deprecationReason": null 336 | }, 337 | { 338 | "name": "startCursor", 339 | "description": "When paginating backwards, the cursor to continue.", 340 | "args": [], 341 | "type": { 342 | "kind": "SCALAR", 343 | "name": "String", 344 | "ofType": null 345 | }, 346 | "isDeprecated": false, 347 | "deprecationReason": null 348 | }, 349 | { 350 | "name": "endCursor", 351 | "description": "When paginating forwards, the cursor to continue.", 352 | "args": [], 353 | "type": { 354 | "kind": "SCALAR", 355 | "name": "String", 356 | "ofType": null 357 | }, 358 | "isDeprecated": false, 359 | "deprecationReason": null 360 | } 361 | ], 362 | "inputFields": null, 363 | "interfaces": [], 364 | "enumValues": null, 365 | "possibleTypes": null 366 | }, 367 | { 368 | "kind": "SCALAR", 369 | "name": "Boolean", 370 | "description": "The `Boolean` scalar type represents `true` or `false`.", 371 | "fields": null, 372 | "inputFields": null, 373 | "interfaces": null, 374 | "enumValues": null, 375 | "possibleTypes": null 376 | }, 377 | { 378 | "kind": "OBJECT", 379 | "name": "PostEdge", 380 | "description": "An edge in a connection.", 381 | "fields": [ 382 | { 383 | "name": "node", 384 | "description": "The item at the end of the edge", 385 | "args": [], 386 | "type": { 387 | "kind": "OBJECT", 388 | "name": "Post", 389 | "ofType": null 390 | }, 391 | "isDeprecated": false, 392 | "deprecationReason": null 393 | }, 394 | { 395 | "name": "cursor", 396 | "description": "A cursor for use in pagination", 397 | "args": [], 398 | "type": { 399 | "kind": "NON_NULL", 400 | "name": null, 401 | "ofType": { 402 | "kind": "SCALAR", 403 | "name": "String", 404 | "ofType": null 405 | } 406 | }, 407 | "isDeprecated": false, 408 | "deprecationReason": null 409 | } 410 | ], 411 | "inputFields": null, 412 | "interfaces": [], 413 | "enumValues": null, 414 | "possibleTypes": null 415 | }, 416 | { 417 | "kind": "OBJECT", 418 | "name": "Post", 419 | "description": "This response Post", 420 | "fields": [ 421 | { 422 | "name": "id", 423 | "description": "The ID of an object", 424 | "args": [], 425 | "type": { 426 | "kind": "NON_NULL", 427 | "name": null, 428 | "ofType": { 429 | "kind": "SCALAR", 430 | "name": "ID", 431 | "ofType": null 432 | } 433 | }, 434 | "isDeprecated": false, 435 | "deprecationReason": null 436 | }, 437 | { 438 | "name": "title", 439 | "description": null, 440 | "args": [], 441 | "type": { 442 | "kind": "SCALAR", 443 | "name": "String", 444 | "ofType": null 445 | }, 446 | "isDeprecated": false, 447 | "deprecationReason": null 448 | }, 449 | { 450 | "name": "content", 451 | "description": null, 452 | "args": [], 453 | "type": { 454 | "kind": "SCALAR", 455 | "name": "String", 456 | "ofType": null 457 | }, 458 | "isDeprecated": false, 459 | "deprecationReason": null 460 | }, 461 | { 462 | "name": "person", 463 | "description": null, 464 | "args": [], 465 | "type": { 466 | "kind": "OBJECT", 467 | "name": "Person", 468 | "ofType": null 469 | }, 470 | "isDeprecated": false, 471 | "deprecationReason": null 472 | } 473 | ], 474 | "inputFields": null, 475 | "interfaces": [ 476 | { 477 | "kind": "INTERFACE", 478 | "name": "Node", 479 | "ofType": null 480 | } 481 | ], 482 | "enumValues": null, 483 | "possibleTypes": null 484 | }, 485 | { 486 | "kind": "OBJECT", 487 | "name": "Store", 488 | "description": null, 489 | "fields": [ 490 | { 491 | "name": "id", 492 | "description": "The ID of an object", 493 | "args": [], 494 | "type": { 495 | "kind": "NON_NULL", 496 | "name": null, 497 | "ofType": { 498 | "kind": "SCALAR", 499 | "name": "ID", 500 | "ofType": null 501 | } 502 | }, 503 | "isDeprecated": false, 504 | "deprecationReason": null 505 | }, 506 | { 507 | "name": "people", 508 | "description": null, 509 | "args": [ 510 | { 511 | "name": "after", 512 | "description": null, 513 | "type": { 514 | "kind": "SCALAR", 515 | "name": "String", 516 | "ofType": null 517 | }, 518 | "defaultValue": null 519 | }, 520 | { 521 | "name": "first", 522 | "description": null, 523 | "type": { 524 | "kind": "SCALAR", 525 | "name": "Int", 526 | "ofType": null 527 | }, 528 | "defaultValue": null 529 | }, 530 | { 531 | "name": "before", 532 | "description": null, 533 | "type": { 534 | "kind": "SCALAR", 535 | "name": "String", 536 | "ofType": null 537 | }, 538 | "defaultValue": null 539 | }, 540 | { 541 | "name": "last", 542 | "description": null, 543 | "type": { 544 | "kind": "SCALAR", 545 | "name": "Int", 546 | "ofType": null 547 | }, 548 | "defaultValue": null 549 | } 550 | ], 551 | "type": { 552 | "kind": "OBJECT", 553 | "name": "PersonConnection", 554 | "ofType": null 555 | }, 556 | "isDeprecated": false, 557 | "deprecationReason": null 558 | }, 559 | { 560 | "name": "posts", 561 | "description": null, 562 | "args": [ 563 | { 564 | "name": "after", 565 | "description": null, 566 | "type": { 567 | "kind": "SCALAR", 568 | "name": "String", 569 | "ofType": null 570 | }, 571 | "defaultValue": null 572 | }, 573 | { 574 | "name": "first", 575 | "description": null, 576 | "type": { 577 | "kind": "SCALAR", 578 | "name": "Int", 579 | "ofType": null 580 | }, 581 | "defaultValue": null 582 | }, 583 | { 584 | "name": "before", 585 | "description": null, 586 | "type": { 587 | "kind": "SCALAR", 588 | "name": "String", 589 | "ofType": null 590 | }, 591 | "defaultValue": null 592 | }, 593 | { 594 | "name": "last", 595 | "description": null, 596 | "type": { 597 | "kind": "SCALAR", 598 | "name": "Int", 599 | "ofType": null 600 | }, 601 | "defaultValue": null 602 | } 603 | ], 604 | "type": { 605 | "kind": "OBJECT", 606 | "name": "PostConnection", 607 | "ofType": null 608 | }, 609 | "isDeprecated": false, 610 | "deprecationReason": null 611 | } 612 | ], 613 | "inputFields": null, 614 | "interfaces": [ 615 | { 616 | "kind": "INTERFACE", 617 | "name": "Node", 618 | "ofType": null 619 | } 620 | ], 621 | "enumValues": null, 622 | "possibleTypes": null 623 | }, 624 | { 625 | "kind": "OBJECT", 626 | "name": "PersonConnection", 627 | "description": "A connection to a list of items.", 628 | "fields": [ 629 | { 630 | "name": "pageInfo", 631 | "description": "Information to aid in pagination.", 632 | "args": [], 633 | "type": { 634 | "kind": "NON_NULL", 635 | "name": null, 636 | "ofType": { 637 | "kind": "OBJECT", 638 | "name": "PageInfo", 639 | "ofType": null 640 | } 641 | }, 642 | "isDeprecated": false, 643 | "deprecationReason": null 644 | }, 645 | { 646 | "name": "edges", 647 | "description": "Information to aid in pagination.", 648 | "args": [], 649 | "type": { 650 | "kind": "LIST", 651 | "name": null, 652 | "ofType": { 653 | "kind": "OBJECT", 654 | "name": "PersonEdge", 655 | "ofType": null 656 | } 657 | }, 658 | "isDeprecated": false, 659 | "deprecationReason": null 660 | } 661 | ], 662 | "inputFields": null, 663 | "interfaces": [], 664 | "enumValues": null, 665 | "possibleTypes": null 666 | }, 667 | { 668 | "kind": "OBJECT", 669 | "name": "PersonEdge", 670 | "description": "An edge in a connection.", 671 | "fields": [ 672 | { 673 | "name": "node", 674 | "description": "The item at the end of the edge", 675 | "args": [], 676 | "type": { 677 | "kind": "OBJECT", 678 | "name": "Person", 679 | "ofType": null 680 | }, 681 | "isDeprecated": false, 682 | "deprecationReason": null 683 | }, 684 | { 685 | "name": "cursor", 686 | "description": "A cursor for use in pagination", 687 | "args": [], 688 | "type": { 689 | "kind": "NON_NULL", 690 | "name": null, 691 | "ofType": { 692 | "kind": "SCALAR", 693 | "name": "String", 694 | "ofType": null 695 | } 696 | }, 697 | "isDeprecated": false, 698 | "deprecationReason": null 699 | } 700 | ], 701 | "inputFields": null, 702 | "interfaces": [], 703 | "enumValues": null, 704 | "possibleTypes": null 705 | }, 706 | { 707 | "kind": "OBJECT", 708 | "name": "Mutation", 709 | "description": null, 710 | "fields": [ 711 | { 712 | "name": "createPost", 713 | "description": null, 714 | "args": [ 715 | { 716 | "name": "input", 717 | "description": null, 718 | "type": { 719 | "kind": "NON_NULL", 720 | "name": null, 721 | "ofType": { 722 | "kind": "INPUT_OBJECT", 723 | "name": "CreatePostInput", 724 | "ofType": null 725 | } 726 | }, 727 | "defaultValue": null 728 | } 729 | ], 730 | "type": { 731 | "kind": "OBJECT", 732 | "name": "CreatePostPayload", 733 | "ofType": null 734 | }, 735 | "isDeprecated": false, 736 | "deprecationReason": null 737 | } 738 | ], 739 | "inputFields": null, 740 | "interfaces": [], 741 | "enumValues": null, 742 | "possibleTypes": null 743 | }, 744 | { 745 | "kind": "INPUT_OBJECT", 746 | "name": "CreatePostInput", 747 | "description": null, 748 | "fields": null, 749 | "inputFields": [ 750 | { 751 | "name": "title", 752 | "description": null, 753 | "type": { 754 | "kind": "NON_NULL", 755 | "name": null, 756 | "ofType": { 757 | "kind": "SCALAR", 758 | "name": "String", 759 | "ofType": null 760 | } 761 | }, 762 | "defaultValue": null 763 | }, 764 | { 765 | "name": "content", 766 | "description": null, 767 | "type": { 768 | "kind": "NON_NULL", 769 | "name": null, 770 | "ofType": { 771 | "kind": "SCALAR", 772 | "name": "String", 773 | "ofType": null 774 | } 775 | }, 776 | "defaultValue": null 777 | }, 778 | { 779 | "name": "person", 780 | "description": null, 781 | "type": { 782 | "kind": "NON_NULL", 783 | "name": null, 784 | "ofType": { 785 | "kind": "SCALAR", 786 | "name": "String", 787 | "ofType": null 788 | } 789 | }, 790 | "defaultValue": null 791 | }, 792 | { 793 | "name": "clientMutationId", 794 | "description": null, 795 | "type": { 796 | "kind": "NON_NULL", 797 | "name": null, 798 | "ofType": { 799 | "kind": "SCALAR", 800 | "name": "String", 801 | "ofType": null 802 | } 803 | }, 804 | "defaultValue": null 805 | } 806 | ], 807 | "interfaces": null, 808 | "enumValues": null, 809 | "possibleTypes": null 810 | }, 811 | { 812 | "kind": "OBJECT", 813 | "name": "CreatePostPayload", 814 | "description": null, 815 | "fields": [ 816 | { 817 | "name": "postEdge", 818 | "description": null, 819 | "args": [], 820 | "type": { 821 | "kind": "OBJECT", 822 | "name": "PostEdge", 823 | "ofType": null 824 | }, 825 | "isDeprecated": false, 826 | "deprecationReason": null 827 | }, 828 | { 829 | "name": "store", 830 | "description": null, 831 | "args": [], 832 | "type": { 833 | "kind": "OBJECT", 834 | "name": "Store", 835 | "ofType": null 836 | }, 837 | "isDeprecated": false, 838 | "deprecationReason": null 839 | }, 840 | { 841 | "name": "clientMutationId", 842 | "description": null, 843 | "args": [], 844 | "type": { 845 | "kind": "NON_NULL", 846 | "name": null, 847 | "ofType": { 848 | "kind": "SCALAR", 849 | "name": "String", 850 | "ofType": null 851 | } 852 | }, 853 | "isDeprecated": false, 854 | "deprecationReason": null 855 | } 856 | ], 857 | "inputFields": null, 858 | "interfaces": [], 859 | "enumValues": null, 860 | "possibleTypes": null 861 | }, 862 | { 863 | "kind": "OBJECT", 864 | "name": "__Schema", 865 | "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.", 866 | "fields": [ 867 | { 868 | "name": "types", 869 | "description": "A list of all types supported by this server.", 870 | "args": [], 871 | "type": { 872 | "kind": "NON_NULL", 873 | "name": null, 874 | "ofType": { 875 | "kind": "LIST", 876 | "name": null, 877 | "ofType": { 878 | "kind": "NON_NULL", 879 | "name": null, 880 | "ofType": { 881 | "kind": "OBJECT", 882 | "name": "__Type" 883 | } 884 | } 885 | } 886 | }, 887 | "isDeprecated": false, 888 | "deprecationReason": null 889 | }, 890 | { 891 | "name": "queryType", 892 | "description": "The type that query operations will be rooted at.", 893 | "args": [], 894 | "type": { 895 | "kind": "NON_NULL", 896 | "name": null, 897 | "ofType": { 898 | "kind": "OBJECT", 899 | "name": "__Type", 900 | "ofType": null 901 | } 902 | }, 903 | "isDeprecated": false, 904 | "deprecationReason": null 905 | }, 906 | { 907 | "name": "mutationType", 908 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 909 | "args": [], 910 | "type": { 911 | "kind": "OBJECT", 912 | "name": "__Type", 913 | "ofType": null 914 | }, 915 | "isDeprecated": false, 916 | "deprecationReason": null 917 | }, 918 | { 919 | "name": "subscriptionType", 920 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 921 | "args": [], 922 | "type": { 923 | "kind": "OBJECT", 924 | "name": "__Type", 925 | "ofType": null 926 | }, 927 | "isDeprecated": false, 928 | "deprecationReason": null 929 | }, 930 | { 931 | "name": "directives", 932 | "description": "A list of all directives supported by this server.", 933 | "args": [], 934 | "type": { 935 | "kind": "NON_NULL", 936 | "name": null, 937 | "ofType": { 938 | "kind": "LIST", 939 | "name": null, 940 | "ofType": { 941 | "kind": "NON_NULL", 942 | "name": null, 943 | "ofType": { 944 | "kind": "OBJECT", 945 | "name": "__Directive" 946 | } 947 | } 948 | } 949 | }, 950 | "isDeprecated": false, 951 | "deprecationReason": null 952 | } 953 | ], 954 | "inputFields": null, 955 | "interfaces": [], 956 | "enumValues": null, 957 | "possibleTypes": null 958 | }, 959 | { 960 | "kind": "OBJECT", 961 | "name": "__Type", 962 | "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.", 963 | "fields": [ 964 | { 965 | "name": "kind", 966 | "description": null, 967 | "args": [], 968 | "type": { 969 | "kind": "NON_NULL", 970 | "name": null, 971 | "ofType": { 972 | "kind": "ENUM", 973 | "name": "__TypeKind", 974 | "ofType": null 975 | } 976 | }, 977 | "isDeprecated": false, 978 | "deprecationReason": null 979 | }, 980 | { 981 | "name": "name", 982 | "description": null, 983 | "args": [], 984 | "type": { 985 | "kind": "SCALAR", 986 | "name": "String", 987 | "ofType": null 988 | }, 989 | "isDeprecated": false, 990 | "deprecationReason": null 991 | }, 992 | { 993 | "name": "description", 994 | "description": null, 995 | "args": [], 996 | "type": { 997 | "kind": "SCALAR", 998 | "name": "String", 999 | "ofType": null 1000 | }, 1001 | "isDeprecated": false, 1002 | "deprecationReason": null 1003 | }, 1004 | { 1005 | "name": "fields", 1006 | "description": null, 1007 | "args": [ 1008 | { 1009 | "name": "includeDeprecated", 1010 | "description": null, 1011 | "type": { 1012 | "kind": "SCALAR", 1013 | "name": "Boolean", 1014 | "ofType": null 1015 | }, 1016 | "defaultValue": "false" 1017 | } 1018 | ], 1019 | "type": { 1020 | "kind": "LIST", 1021 | "name": null, 1022 | "ofType": { 1023 | "kind": "NON_NULL", 1024 | "name": null, 1025 | "ofType": { 1026 | "kind": "OBJECT", 1027 | "name": "__Field", 1028 | "ofType": null 1029 | } 1030 | } 1031 | }, 1032 | "isDeprecated": false, 1033 | "deprecationReason": null 1034 | }, 1035 | { 1036 | "name": "interfaces", 1037 | "description": null, 1038 | "args": [], 1039 | "type": { 1040 | "kind": "LIST", 1041 | "name": null, 1042 | "ofType": { 1043 | "kind": "NON_NULL", 1044 | "name": null, 1045 | "ofType": { 1046 | "kind": "OBJECT", 1047 | "name": "__Type", 1048 | "ofType": null 1049 | } 1050 | } 1051 | }, 1052 | "isDeprecated": false, 1053 | "deprecationReason": null 1054 | }, 1055 | { 1056 | "name": "possibleTypes", 1057 | "description": null, 1058 | "args": [], 1059 | "type": { 1060 | "kind": "LIST", 1061 | "name": null, 1062 | "ofType": { 1063 | "kind": "NON_NULL", 1064 | "name": null, 1065 | "ofType": { 1066 | "kind": "OBJECT", 1067 | "name": "__Type", 1068 | "ofType": null 1069 | } 1070 | } 1071 | }, 1072 | "isDeprecated": false, 1073 | "deprecationReason": null 1074 | }, 1075 | { 1076 | "name": "enumValues", 1077 | "description": null, 1078 | "args": [ 1079 | { 1080 | "name": "includeDeprecated", 1081 | "description": null, 1082 | "type": { 1083 | "kind": "SCALAR", 1084 | "name": "Boolean", 1085 | "ofType": null 1086 | }, 1087 | "defaultValue": "false" 1088 | } 1089 | ], 1090 | "type": { 1091 | "kind": "LIST", 1092 | "name": null, 1093 | "ofType": { 1094 | "kind": "NON_NULL", 1095 | "name": null, 1096 | "ofType": { 1097 | "kind": "OBJECT", 1098 | "name": "__EnumValue", 1099 | "ofType": null 1100 | } 1101 | } 1102 | }, 1103 | "isDeprecated": false, 1104 | "deprecationReason": null 1105 | }, 1106 | { 1107 | "name": "inputFields", 1108 | "description": null, 1109 | "args": [], 1110 | "type": { 1111 | "kind": "LIST", 1112 | "name": null, 1113 | "ofType": { 1114 | "kind": "NON_NULL", 1115 | "name": null, 1116 | "ofType": { 1117 | "kind": "OBJECT", 1118 | "name": "__InputValue", 1119 | "ofType": null 1120 | } 1121 | } 1122 | }, 1123 | "isDeprecated": false, 1124 | "deprecationReason": null 1125 | }, 1126 | { 1127 | "name": "ofType", 1128 | "description": null, 1129 | "args": [], 1130 | "type": { 1131 | "kind": "OBJECT", 1132 | "name": "__Type", 1133 | "ofType": null 1134 | }, 1135 | "isDeprecated": false, 1136 | "deprecationReason": null 1137 | } 1138 | ], 1139 | "inputFields": null, 1140 | "interfaces": [], 1141 | "enumValues": null, 1142 | "possibleTypes": null 1143 | }, 1144 | { 1145 | "kind": "ENUM", 1146 | "name": "__TypeKind", 1147 | "description": "An enum describing what kind of type a given `__Type` is.", 1148 | "fields": null, 1149 | "inputFields": null, 1150 | "interfaces": null, 1151 | "enumValues": [ 1152 | { 1153 | "name": "SCALAR", 1154 | "description": "Indicates this type is a scalar.", 1155 | "isDeprecated": false, 1156 | "deprecationReason": null 1157 | }, 1158 | { 1159 | "name": "OBJECT", 1160 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 1161 | "isDeprecated": false, 1162 | "deprecationReason": null 1163 | }, 1164 | { 1165 | "name": "INTERFACE", 1166 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 1167 | "isDeprecated": false, 1168 | "deprecationReason": null 1169 | }, 1170 | { 1171 | "name": "UNION", 1172 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 1173 | "isDeprecated": false, 1174 | "deprecationReason": null 1175 | }, 1176 | { 1177 | "name": "ENUM", 1178 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 1179 | "isDeprecated": false, 1180 | "deprecationReason": null 1181 | }, 1182 | { 1183 | "name": "INPUT_OBJECT", 1184 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 1185 | "isDeprecated": false, 1186 | "deprecationReason": null 1187 | }, 1188 | { 1189 | "name": "LIST", 1190 | "description": "Indicates this type is a list. `ofType` is a valid field.", 1191 | "isDeprecated": false, 1192 | "deprecationReason": null 1193 | }, 1194 | { 1195 | "name": "NON_NULL", 1196 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 1197 | "isDeprecated": false, 1198 | "deprecationReason": null 1199 | } 1200 | ], 1201 | "possibleTypes": null 1202 | }, 1203 | { 1204 | "kind": "OBJECT", 1205 | "name": "__Field", 1206 | "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.", 1207 | "fields": [ 1208 | { 1209 | "name": "name", 1210 | "description": null, 1211 | "args": [], 1212 | "type": { 1213 | "kind": "NON_NULL", 1214 | "name": null, 1215 | "ofType": { 1216 | "kind": "SCALAR", 1217 | "name": "String", 1218 | "ofType": null 1219 | } 1220 | }, 1221 | "isDeprecated": false, 1222 | "deprecationReason": null 1223 | }, 1224 | { 1225 | "name": "description", 1226 | "description": null, 1227 | "args": [], 1228 | "type": { 1229 | "kind": "SCALAR", 1230 | "name": "String", 1231 | "ofType": null 1232 | }, 1233 | "isDeprecated": false, 1234 | "deprecationReason": null 1235 | }, 1236 | { 1237 | "name": "args", 1238 | "description": null, 1239 | "args": [], 1240 | "type": { 1241 | "kind": "NON_NULL", 1242 | "name": null, 1243 | "ofType": { 1244 | "kind": "LIST", 1245 | "name": null, 1246 | "ofType": { 1247 | "kind": "NON_NULL", 1248 | "name": null, 1249 | "ofType": { 1250 | "kind": "OBJECT", 1251 | "name": "__InputValue" 1252 | } 1253 | } 1254 | } 1255 | }, 1256 | "isDeprecated": false, 1257 | "deprecationReason": null 1258 | }, 1259 | { 1260 | "name": "type", 1261 | "description": null, 1262 | "args": [], 1263 | "type": { 1264 | "kind": "NON_NULL", 1265 | "name": null, 1266 | "ofType": { 1267 | "kind": "OBJECT", 1268 | "name": "__Type", 1269 | "ofType": null 1270 | } 1271 | }, 1272 | "isDeprecated": false, 1273 | "deprecationReason": null 1274 | }, 1275 | { 1276 | "name": "isDeprecated", 1277 | "description": null, 1278 | "args": [], 1279 | "type": { 1280 | "kind": "NON_NULL", 1281 | "name": null, 1282 | "ofType": { 1283 | "kind": "SCALAR", 1284 | "name": "Boolean", 1285 | "ofType": null 1286 | } 1287 | }, 1288 | "isDeprecated": false, 1289 | "deprecationReason": null 1290 | }, 1291 | { 1292 | "name": "deprecationReason", 1293 | "description": null, 1294 | "args": [], 1295 | "type": { 1296 | "kind": "SCALAR", 1297 | "name": "String", 1298 | "ofType": null 1299 | }, 1300 | "isDeprecated": false, 1301 | "deprecationReason": null 1302 | } 1303 | ], 1304 | "inputFields": null, 1305 | "interfaces": [], 1306 | "enumValues": null, 1307 | "possibleTypes": null 1308 | }, 1309 | { 1310 | "kind": "OBJECT", 1311 | "name": "__InputValue", 1312 | "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.", 1313 | "fields": [ 1314 | { 1315 | "name": "name", 1316 | "description": null, 1317 | "args": [], 1318 | "type": { 1319 | "kind": "NON_NULL", 1320 | "name": null, 1321 | "ofType": { 1322 | "kind": "SCALAR", 1323 | "name": "String", 1324 | "ofType": null 1325 | } 1326 | }, 1327 | "isDeprecated": false, 1328 | "deprecationReason": null 1329 | }, 1330 | { 1331 | "name": "description", 1332 | "description": null, 1333 | "args": [], 1334 | "type": { 1335 | "kind": "SCALAR", 1336 | "name": "String", 1337 | "ofType": null 1338 | }, 1339 | "isDeprecated": false, 1340 | "deprecationReason": null 1341 | }, 1342 | { 1343 | "name": "type", 1344 | "description": null, 1345 | "args": [], 1346 | "type": { 1347 | "kind": "NON_NULL", 1348 | "name": null, 1349 | "ofType": { 1350 | "kind": "OBJECT", 1351 | "name": "__Type", 1352 | "ofType": null 1353 | } 1354 | }, 1355 | "isDeprecated": false, 1356 | "deprecationReason": null 1357 | }, 1358 | { 1359 | "name": "defaultValue", 1360 | "description": "A GraphQL-formatted string representing the default value for this input value.", 1361 | "args": [], 1362 | "type": { 1363 | "kind": "SCALAR", 1364 | "name": "String", 1365 | "ofType": null 1366 | }, 1367 | "isDeprecated": false, 1368 | "deprecationReason": null 1369 | } 1370 | ], 1371 | "inputFields": null, 1372 | "interfaces": [], 1373 | "enumValues": null, 1374 | "possibleTypes": null 1375 | }, 1376 | { 1377 | "kind": "OBJECT", 1378 | "name": "__EnumValue", 1379 | "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.", 1380 | "fields": [ 1381 | { 1382 | "name": "name", 1383 | "description": null, 1384 | "args": [], 1385 | "type": { 1386 | "kind": "NON_NULL", 1387 | "name": null, 1388 | "ofType": { 1389 | "kind": "SCALAR", 1390 | "name": "String", 1391 | "ofType": null 1392 | } 1393 | }, 1394 | "isDeprecated": false, 1395 | "deprecationReason": null 1396 | }, 1397 | { 1398 | "name": "description", 1399 | "description": null, 1400 | "args": [], 1401 | "type": { 1402 | "kind": "SCALAR", 1403 | "name": "String", 1404 | "ofType": null 1405 | }, 1406 | "isDeprecated": false, 1407 | "deprecationReason": null 1408 | }, 1409 | { 1410 | "name": "isDeprecated", 1411 | "description": null, 1412 | "args": [], 1413 | "type": { 1414 | "kind": "NON_NULL", 1415 | "name": null, 1416 | "ofType": { 1417 | "kind": "SCALAR", 1418 | "name": "Boolean", 1419 | "ofType": null 1420 | } 1421 | }, 1422 | "isDeprecated": false, 1423 | "deprecationReason": null 1424 | }, 1425 | { 1426 | "name": "deprecationReason", 1427 | "description": null, 1428 | "args": [], 1429 | "type": { 1430 | "kind": "SCALAR", 1431 | "name": "String", 1432 | "ofType": null 1433 | }, 1434 | "isDeprecated": false, 1435 | "deprecationReason": null 1436 | } 1437 | ], 1438 | "inputFields": null, 1439 | "interfaces": [], 1440 | "enumValues": null, 1441 | "possibleTypes": null 1442 | }, 1443 | { 1444 | "kind": "OBJECT", 1445 | "name": "__Directive", 1446 | "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.", 1447 | "fields": [ 1448 | { 1449 | "name": "name", 1450 | "description": null, 1451 | "args": [], 1452 | "type": { 1453 | "kind": "NON_NULL", 1454 | "name": null, 1455 | "ofType": { 1456 | "kind": "SCALAR", 1457 | "name": "String", 1458 | "ofType": null 1459 | } 1460 | }, 1461 | "isDeprecated": false, 1462 | "deprecationReason": null 1463 | }, 1464 | { 1465 | "name": "description", 1466 | "description": null, 1467 | "args": [], 1468 | "type": { 1469 | "kind": "SCALAR", 1470 | "name": "String", 1471 | "ofType": null 1472 | }, 1473 | "isDeprecated": false, 1474 | "deprecationReason": null 1475 | }, 1476 | { 1477 | "name": "args", 1478 | "description": null, 1479 | "args": [], 1480 | "type": { 1481 | "kind": "NON_NULL", 1482 | "name": null, 1483 | "ofType": { 1484 | "kind": "LIST", 1485 | "name": null, 1486 | "ofType": { 1487 | "kind": "NON_NULL", 1488 | "name": null, 1489 | "ofType": { 1490 | "kind": "OBJECT", 1491 | "name": "__InputValue" 1492 | } 1493 | } 1494 | } 1495 | }, 1496 | "isDeprecated": false, 1497 | "deprecationReason": null 1498 | }, 1499 | { 1500 | "name": "onOperation", 1501 | "description": null, 1502 | "args": [], 1503 | "type": { 1504 | "kind": "NON_NULL", 1505 | "name": null, 1506 | "ofType": { 1507 | "kind": "SCALAR", 1508 | "name": "Boolean", 1509 | "ofType": null 1510 | } 1511 | }, 1512 | "isDeprecated": false, 1513 | "deprecationReason": null 1514 | }, 1515 | { 1516 | "name": "onFragment", 1517 | "description": null, 1518 | "args": [], 1519 | "type": { 1520 | "kind": "NON_NULL", 1521 | "name": null, 1522 | "ofType": { 1523 | "kind": "SCALAR", 1524 | "name": "Boolean", 1525 | "ofType": null 1526 | } 1527 | }, 1528 | "isDeprecated": false, 1529 | "deprecationReason": null 1530 | }, 1531 | { 1532 | "name": "onField", 1533 | "description": null, 1534 | "args": [], 1535 | "type": { 1536 | "kind": "NON_NULL", 1537 | "name": null, 1538 | "ofType": { 1539 | "kind": "SCALAR", 1540 | "name": "Boolean", 1541 | "ofType": null 1542 | } 1543 | }, 1544 | "isDeprecated": false, 1545 | "deprecationReason": null 1546 | } 1547 | ], 1548 | "inputFields": null, 1549 | "interfaces": [], 1550 | "enumValues": null, 1551 | "possibleTypes": null 1552 | } 1553 | ], 1554 | "directives": [ 1555 | { 1556 | "name": "include", 1557 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 1558 | "args": [ 1559 | { 1560 | "name": "if", 1561 | "description": "Included when true.", 1562 | "type": { 1563 | "kind": "NON_NULL", 1564 | "name": null, 1565 | "ofType": { 1566 | "kind": "SCALAR", 1567 | "name": "Boolean", 1568 | "ofType": null 1569 | } 1570 | }, 1571 | "defaultValue": null 1572 | } 1573 | ], 1574 | "onOperation": false, 1575 | "onFragment": true, 1576 | "onField": true 1577 | }, 1578 | { 1579 | "name": "skip", 1580 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 1581 | "args": [ 1582 | { 1583 | "name": "if", 1584 | "description": "Skipped when true.", 1585 | "type": { 1586 | "kind": "NON_NULL", 1587 | "name": null, 1588 | "ofType": { 1589 | "kind": "SCALAR", 1590 | "name": "Boolean", 1591 | "ofType": null 1592 | } 1593 | }, 1594 | "defaultValue": null 1595 | } 1596 | ], 1597 | "onOperation": false, 1598 | "onFragment": true, 1599 | "onField": true 1600 | } 1601 | ] 1602 | } 1603 | } 1604 | } --------------------------------------------------------------------------------