GraphQL.jsのチュートリアルをただやってみただけの投稿

私は初心者です。こちらのチュートリアルをみながら自分で試したことをただメモ用に書いていくだけです。よろしくお願いいたします。

[Getting Started With GraphQL.js | GraphQL.js Tutorial](https://graphql.org/graphql-js/)

---

# Getting Started With GraphQL.js
https://graphql.org/graphql-js/

GraphQL.js のインストール

```
yarn add graphql
```

```server.js
const { graphql, buildSchema } = require('graphql');

// GraphQLスキーマ言語を使用して、スキーマを構築
const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// ルートは各APIエンドポイントにリゾルバー機能を提供する
const root = {
  hello: () => {
    return 'Hello world!';
  },
};

// GraphQLクエリ '{ hello }' を実行してレスポンスを出力
graphql(schema, '{ hello }', root).then((response) => {
    console.log(response);
});
```

```console
$ node server.js 
{ data: [Object: null prototype] { hello: 'Hello world!' } }
```

# Running an Express GraphQL Server

https://graphql.org/graphql-js/running-an-express-graphql-server/

expressで実行するために必要なライブラリをインストール

```
yarn add express express-graphql graphql
```

apiサーバ作成

```server.js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const root = {
  hello: () => {
    return 'Hello world!';
  },
};

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');
```

```console
$ node server.js 
Running a GraphQL API server at http://localhost:4000/graphql
```

`graphiql: true` としたので、[graphiql](https://github.com/graphql/graphiql) を使用できます

http://localhost:4000/graphql

<img width="738" alt="Screen Shot 2020-03-21 at 17.06.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/8ee84699-ccd0-5df8-34f5-4cbb3fc53f22.png">

# GraphQL Clients

https://graphql.org/graphql-js/graphql-clients/

- curlでgraphqlを実行する

```console
$ curl -X POST -H "Content-Type: application/json" -d '{"query": "{ hello }"}' http://localhost:4000/graphql
{"data":{"hello":"Hello world!"}}
```

- GUIで実行する場合は[GraphiQL](https://github.com/graphql/graphiql)や[Insomnia](https://github.com/Kong/insomnia)などを使う


- ブラウザから実行する

http://localhost:4000/graphql のdevtoolを開いて、以下をコピペする

```js
fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({query: "{ hello }"})
})
  .then(r => r.json())
  .then(data => console.log('data returned:', data));
```

<img width="754" alt="Screen Shot 2020-03-22 at 19.19.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/63b06c8e-944d-d60b-8c53-c832d2f680b0.png">

# Basic Types

https://graphql.org/graphql-js/basic-types/

> GraphQLスキーマ言語は、String、Int、Float、Boolean、およびIDのスカラータイプをサポートしているため、buildSchemaに渡すスキーマでこれらを直接使用できます。

> デフォルトでは、すべての型はnull値を許可します。スカラー型のいずれかとしてnullを返すことは正当です。 感嘆符を使用して、型をnullにできないことを示すため、String！ null不可の文字列です。

```js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    quoteOfTheDay: String
    random: Float!
    rollThreeDice: [Int]
  }
`);

const root = {
  quoteOfTheDay: () => {
    return Math.random() < 0.5 ? 'Take it easy' : 'Salvation lies within';
  },
  random: () => {
    return Math.random();
  },
  rollThreeDice: () => {
    return [1, 2, 3].map(_ => 1 + Math.floor(Math.random() * 6));
  },
};

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```

指定の型で取得できたことを確認できました

<img width="764" alt="Screen Shot 2020-03-22 at 19.40.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/35407e34-0915-69f9-b99f-7c75208e97b8.png">

# Passing Arguments

https://graphql.org/graphql-js/passing-arguments/

numDiceとnumSidesを引数にとるrollDiceを定義

```
type Query {
  rollDice(numDice: Int!, numSides: Int): [Int]
}
```

```js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    rollDice(numDice: Int!, numSides: Int): [Int]
  }
`);

const root = {
  rollDice: ({numDice, numSides}) => {
    const output = [];
    for (let i = 0; i < numDice; i++) {
      output.push(1 + Math.floor(Math.random() * (numSides || 6)));
    }
    return output;
  }
};

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```

引数付きで遅れることを確認

<img width="764" alt="Screen Shot 2020-03-22 at 19.53.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/9c4b0ddc-c9e9-a519-0cb2-b3f29ba438e1.png">

コードで引数を設定するときは、`$構文`を使う方が良い

```js
const dice = 3;
const sides = 6;
const query = `query RollDice($dice: Int!, $sides: Int) {
  rollDice(numDice: $dice, numSides: $sides)
}`;

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({
    query,
    variables: { dice, sides },
  })
})
  .then(r => r.json())
  .then(data => console.log('data returned:', data));
```

<img width="454" alt="Screen Shot 2020-03-22 at 20.00.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/a248f5f8-5cdf-8724-d9b1-60b413d96596.png">

# Object Types

https://graphql.org/graphql-js/object-types/

オブジェクトタイプを指定して、値を返すようにします

```server.js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type RandomDie {
    numSides: Int!
    rollOnce: Int!
    roll(numRolls: Int!): [Int]
  }

  type Query {
    getDie(numSides: Int): RandomDie
  }
`);

class RandomDie {
  constructor(numSides) {
    this.numSides = numSides;
  }

  rollOnce() {
    return 1 + Math.floor(Math.random() * this.numSides);
  }

  roll({numRolls}) {
    const output = [];
    for (let i = 0; i < numRolls; i++) {
      output.push(this.rollOnce());
    }
    return output;
  }
}

const root = {
  getDie: ({numSides}) => {
    return new RandomDie(numSides || 6);
  }
}

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```

```
{
  getDie(numSides: 6) {
    numSides
    rollOnce
    roll(numRolls: 3)
  }
}
```

<img width="820" alt="Screen Shot 2020-03-22 at 20.17.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/32209c6d-b1d8-4a24-a145-a1fc73760bf9.png">

# Mutations and Input Types

https://graphql.org/graphql-js/mutations-and-input-types/

データの作成や更新は`query`ではなく`mutatison`で送る。
スキーマを簡単にするために、typeキーワードの代わりにinputキーワードを使用すると良いようです。

```server.js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  input MessageInput {
    content: String
    author: String
  }

  type Message {
    id: ID!
    content: String
    author: String
  }

  type Query {
    getMessage(id: ID!): Message
  }

  type Mutation {
    createMessage(input: MessageInput): Message
    updateMessage(id: ID!, input: MessageInput): Message
  }
`);

class Message {
  constructor(id, {content, author}) {
    this.id = id;
    this.content = content;
    this.author = author;
  }
}

const fakeDatabase = {};

const root = {
  getMessage: ({id}) => {
    if (!fakeDatabase[id]) {
      throw new Error('no message exists with id ' + id);
    }
    return new Message(id, fakeDatabase[id]);
  },
  createMessage: ({input}) => {
    const id = require('crypto').randomBytes(10).toString('hex');

    fakeDatabase[id] = input;
    return new Message(id, input);
  },
  updateMessage: ({id, input}) => {
    if (!fakeDatabase[id]) {
      throw new Error('no message exists with id ' + id);
    }
    fakeDatabase[id] = input;
    return new Message(id, input);
  },
};

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000, () => {
  console.log('Running a GraphQL API server at localhost:4000/graphql');
});
```

データ作成を確認

<img width="820" alt="Screen Shot 2020-03-22 at 20.32.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/bc87a187-1b5b-fecd-2477-cb54083c8673.png">

データ更新を確認

<img width="820" alt="Screen Shot 2020-03-22 at 20.34.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/aed91c0e-e707-a6b1-743b-ffd43618ab6e.png">