Node.js × Expressで始めるGraphQL入門

# 概要
GraphQLを初めて実装してみた時の手順と、サンプルソースコードを記載しています。

# 環境
- macOS Big Sur 11.1
- node v14.12.0
- yarn 1.22.10
- express 4.16.1
- Docker version 19.03.13
- docker-compose version 1.27.4

# サンプルソースコード
[hayatoiwashita/express-graphql](https://github.com/hayatoiwashita/express-graphql)

# 手順
## 1. プロジェクトの雛形を作成
express-generatorを使用した。
`--git`オプションは`.gitignore`を追加するためのオプション。

```
$ express --view=pug --git express-graphql
```
参考: [Express のアプリケーション生成プログラム](https://expressjs.com/ja/starter/generator.html)

## 2. ライブラリのインストール
GraphQL関連のライブラリは最低限下記があればOK。

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

[サンプル](https://github.com/hayatoiwashita/express-graphql)では下記も追加している(本題から逸れるので割愛)

```
# ホットリロード
$ yarn add --dev nodemon
# コード整形
$ yarn add prettier --dev --exact
$ yarn add eslint --dev
$ yarn add --dev eslint-config-prettier
$ yarn add --dev eslint-plugin-prettier
# commit時にコード整形を実行
$ yarn add --dev lint-staged
$ yarn add --dev husky
```

## 3. サンプルデータの用意
`/data/users.js`を追加して、DBの代わりに使用した。
(内容は[サンプルソースコードのusers.js](https://github.com/hayatoiwashita/express-graphql/blob/master/data/users.js)を参照)

## 4. schemaを定義
`/schema/schema.graphql`を作成してschemaを定義。
今回は参照系クエリのみ実装した。
データ構造は、ユーザとそれに紐づく投稿といった形。

```schema.graphql
const { buildSchema } = require("graphql");

const schema = buildSchema(`
    type Query {
        users: [User!]!,
        user(id: Int!): User!
    }

    type User {
        id: ID!
        name: String!
        email: String
        posts: [Post!]
    }

    type Post {
        id: ID!
        title: String!
        published: Boolean!
        link: String
        author: User!
    }
`);

module.exports = schema;
```

## 5. リゾルバを作成
データ操作を行う`/src/resolvers.js`を作成。
上述の通り、今回は参照系のみ。

```resolvers.js
const { Users } = require('../data/users');

const resolvers = {
  users: async (_) => {
    console.log('come here');
    return Users;
  },
  user: async ({ id }, context) => {
    return Users.find((user) => user.id == id);
  },
};

module.exports = resolvers;
```

## 6. app.js を修正
エンドポイント`/graphql`を定義。

```app.js
// var createError = require('http-errors');
var express = require('express');
var { graphqlHTTP } = require('express-graphql');
var resolvers = require('./src/resolvers');
var schema = require('./schema/schema.graphql');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue: resolvers,
    graphiql: true,
  })
);

module.exports = app;
```

## 7. [参考] コンテナ化
`Dockerfile`と`docker-compose.yml`を追加。

```Dockerfile
FROM node:14.15.4-slim

WORKDIR /usr/local/src/express-graphql
ADD . .
RUN yarn install
CMD [ "yarn", "start" ]
```

```docker-compose.yml
version: '3'

services:
  graphql:
    build: .
    container_name: graphql-container
    ports:
      - "3000:3000"
```

## 8. Expressの起動
起動コマンドを実行。

```
# コンテナで実行
$ docker-compose up

# コンテナを使用せず実行
$ yarn start
```

## 9. クエリを実行

http://localhost:3000/graphql をブラウザで開く。
![screencapture-localhost-3000-graphql-2021-01-16-16_08_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883649/ebd07efe-1c11-6db4-fdfb-f2d2b12f1a14.png)

【**サンプルクエリ**】
内容としては、ユーザID=1のユーザの名前と、そのユーザの投稿(posts)のIDとタイトルを取得するというもの。

```
{
  user(id:1) {
    name
    posts {
      id
      title
    }
  }
}
```

実行結果

```
{
  "data": {
    "user": {
      "name": "Fikayo Adepoju",
      "posts": [
        {
          "id": "1",
          "title": "Debugging an Ionic Android App Using Chrome Dev Tools"
        },
        {
          "id": "2",
          "title": "Hosting a Laravel Application on Azure Web App"
        }
      ]
    }
  }
}
```

![screencapture-localhost-3000-graphql-2021-01-17-15_09_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883649/c810e4a0-2c37-e923-bbce-842b8e916bf8.png)

# 参考
- [GraphQLをNode.jsとexpressでためしてみる](https://dev.classmethod.jp/articles/graphql-tutorial-nodejsexpress/)