【GraphQL】Neo4jを弄くり回すGraphQLを30分で作ってみた

# はじめに

MySQLなど有名なリレーショナルデータベースを対象にしたGraphQLの解説はよくみるが、**グラフデータベースを対象にした記事が少ない**。今回はNodejsで、**Neo4jを弄くり回すGraphQLを30分で実装します。**

**Neo4jとGraphQLがどのようなものかは知っている前提で解説します**ので、知らない方はまずググって下さい。

※_Neo4jはオープンソースの最も人気のあるグラフデータベースです。_

# 条件
ローカル環境でNeo4jを動かすことができる。
GraphQL、Neo4jの使い方の基礎は知っている。

# 環境を整える

### Neo4jにAPOCを導入

**APOCはNeo4jの拡張ライブラリで、Cypherで使える表現が増えます**。GraphQLからデータを更新する際に使用します。

Neo4j公式サイトに導入方法が書かれています。
[APOC User Guide 3.4.0.4](https://neo4j-contrib.github.io/neo4j-apoc-procedures/)

### ES6でJavascriptを書けるようにする

まず、neo4j-graphql-sampleフォルダを作成します。

import等、**ES6のJavascriptをNodejsで実行できるコードへトランスパイルするためにbabelを導入します**。

詳しくは[こちらの記事](https://daiki-sekiguchi.com/2018/09/11/nodejs-how-to-use-es6/)見てください。

```bash
$ mkdir neo4j-graphql-sample
$ cd neo4j-graphql-sample
$ npm init -y
$ npm install --save-dev babel-cli babel-preset-env
```

.babelrcを作成します。

```json:.babelrc
{
  "presets": [
    [
      "env", {
        "targets": {
            "node": "current"
        }
      }
    ]
  ]
}
```

package.jsonを修正します。

```json:package.json
{
  ...
  "scripts": {
    "start": "babel-node index.js"
  },
  ...
}
```

### GraphQL実装に必要なライブラリをインストール

GraphQL実装には、Apolloというライブラリを使用します。
**Apolloを使用すると簡単にGraphQLサーバーとフロントエンドをつなぐことができます**。GraphQL実装の話はもはやApolloサーバー実装の話になっているほどメジャーなライブラリです。

また、neo4jを弄り回すので、`neo4j-driver`と`neo4j-graphql-js`を利用します。

```bash
$ npm install --save apollo-server-express \
apollo-errors \
express \
cors \
graphql \
graphql-tag \
graphql-tools \
neo4j-driver \
neo4j-graphql-js
```

# GraphQLを作る

graphQL実装のポイントとして、**クエリの型を定義したスキーマ、返すデータを作るリソルバー、Neo4jとデータのやりとりを行うドライバーを用意し、Apolloサーバーに設定します**。

Apolloサーバーは今回はexpressを利用して作ります。

それでは、index.jsを作成し実際にコードを書いていきます。

```js:index.js
import { makeAugmentedSchema } from 'neo4j-graphql-js'
import { ApolloServer } from 'apollo-server-express'
import express from 'express'
import bodyParser from 'body-parser'
import cors from 'cors'
import { v1 as neo4j } from 'neo4j-driver'
import { typeDefs, resolvers } from './schema'

// スキーマの作成
// typeDefsにQueryとMutationの型を定義する
const schema = makeAugmentedSchema({
  typeDefs,
  config: {
    query: true,
    mutation: false
  }
})

const neo4jUri = process.env.NEO4J_URI || 'bolt://localhost:7687'
const neo4jUser = process.env.NEO4J_USER || 'neo4j'
const neo4jPassword = process.env.NEO4J_PASSWORD || 'neo4j'

// ドライバーの作成
const driver = neo4j.driver(neo4jUri, neo4j.auth.basic(neo4jUser, neo4jPassword))

// expressサーバーの作成
const app = express()
app.use(bodyParser.json())
app.use(cors())

const playgroundEndpoint = '/graphql'

// Apolloサーバーの作成
// playgroundは試しにクエリを流せるエディタ、後で使用する
const server = new ApolloServer({
  schema,
  resolvers,
  context: ({ req }) => {
    return {
      driver,
      req,
    }
  },
  introspection: true,
  playground: {
    endpoint: playgroundEndpoint,
    settings: {
      'editor.theme': 'light'
    }
  }
})

// expressとapolloサーバーを繋げる
server.applyMiddleware({ app, path: '/' })

app.listen(4000, () => {
  console.log(`http://localhost:4000/graphql`)
})
```

schema.jsを作成します。

**schema.jsにはスキーマの型とリソルバーを定義します**。

スキーマの型では@relationと@cypherのディレクティブを使用できます。@relationはNeo4jのノード同士のエッジの繋がりを定義でき、@cypherはCypherクエリをNeo4jに直接実行させることができます。

リソルバーとは、スキーマをもとに、返すデータを作成します。
リソルバーは`neo4jgraphql`を使えば、よしなにQueryかMutationかを判断しDB操作後、返すデータを作成してくれます。

今回は、**ノードとして「User」と「Article」、エッジとして「WRITE」を追加、参照できるQueryとMutationの型を作ります**。

```js:schema.js
import { neo4jgraphql } from 'neo4j-graphql-js'

export const typeDefs = `
  type Article {
    _id: ID
    uuid: ID!
    title: String!
    description: String!
    created_at: Date!
    write_user: User @relation(name: "WRITE", direction: "IN")
  }

  type User {
    _id: ID
    uuid: ID!
    name: String!
    created_at: Date!
    write_articles: [Article] @relation(name: "WRITE", direction: "OUT")
  }

  type Query {
    Article(_id: ID, title: String, description: String, created_at: Date): [Article]
    User(_id: ID, uid: ID, name: String, avatar: String, created_at: Int): [User]
  }

  type Mutation {
    writeArticle(title: String!, description: String! user_uuid: ID!): Article
      @cypher(statement:"MATCH (u:User {uuid: $user_uuid}) MERGE (u)-[r:WRITE]->(n:Article {uuid: apoc.create.uuid(), title: $title, description: $description, created_at: apoc.date.format(apoc.date.add(timestamp(), 'ms', 9, 'h'), 'ms')}) return n")

    createUser(name: String!): User
      @cypher(statement:"CREATE (n:User {uuid: apoc.create.uuid(), name: $name, created_at: apoc.date.format(apoc.date.add(timestamp(), 'ms', 9, 'h'), 'ms')}) return n")
  }
`

export const resolvers = {
  Query: {
    Article(obj, args, ctx, info) {
      return neo4jgraphql(obj, args, ctx, info)
    },
    User(obj, args, ctx, info) {
      return neo4jgraphql(obj, args, ctx, info)
    },
  },
  Mutation: {
    writeArticle(obj, args, ctx, info) {
      return neo4jgraphql(obj, args, ctx, info)
    },
    createUser(obj, args, ctx, info) {
      return neo4jgraphql(obj, args, ctx, info)
    },
  }
}

```

# PlaygroundでGraphQLを使ってみる

Neo4jを起動しておき、`npm start`で先ほど実装したApolloサーバーを動かしてみましょう。

```bash
$ npm start

> neo4j-graphql-sample@1.0.0 start ~/neo4j-graphql-sample
> babel-node index.js

http://localhost:4000/graphql
```

Apolloサーバーの立ち上げに成功すると、**localhost:4000/graphqlにアクセスすることでPlaygroundという、GraphQLのクエリをブラウザ上で試せるエディタを開くことができます。**

GraphQLでクエリの書き方がわからないかたは下記でさらっと学んでください。
[GraphQLのクエリを基礎から整理してみた](https://qiita.com/shunp/items/d85fc47b33e1b3a88167)

**createUserのMutationでUserを追加してみましょう**。

<img width="1436" alt="スクリーンショット 2019-01-26 0.20.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/8dae4ec3-9c85-f935-5ca1-ec199b243157.png">

**writeArticleのMutationでArticleを追加してみましょう**。
createUser実行時取得したUserのuuidをuser_uuidに入れて実行しましょう。

<img width="1432" alt="スクリーンショット 2019-01-26 1.11.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/14ad31f4-cc8e-a0ca-1cf8-fd235395fd7d.png">

ArticleのQueryで参照してみましょう。

<img width="1440" alt="スクリーンショット 2019-01-26 1.29.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/586c110c-abe5-03b4-5422-6d59b1a9e5f0.png">

ちなみに、下記のようなクエリを流すとArticleを書いたUserの情報も参照することが可能です。

<img width="1440" alt="スクリーンショット 2019-01-26 1.31.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/458bba0e-c840-8545-a058-29797ae40b99.png">


現時点で**Neo4jにはノードとして「User」、「Article」、エッジとして「WRITE」が登録されている**はずです。ブラウザでNeo4jエディタにアクセスし、確認してみましょう。

<img width="1315" alt="スクリーンショット 2019-01-26 1.16.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/26115cf5-5374-9432-ce87-7bdc922908d6.png">

# さいごに

「User」と「Article」を追加、参照するGraphQLを作ることができました。

サンプルコードgithubにあげておきました。

[https://github.com/kousaku-maron/neo4j-graphql-sample](https://github.com/kousaku-maron/neo4j-graphql-sample)

今回はMutationを独自で組み込みましたが、エッジの「WRITE」を自動でつける必要がないのであれば、`makeAugmentedSchema`の`config`の`mutation`をtrueにすれば、独自で作らなくても勝手にMutationが使えるようになります。

ここでは解説していない認証・認可や、GraphQLを実装する際に考えるべき点をまとめた記事も書いてます。よかったらのぞいてみてください。

[【GraphQL】GraphQL実装で押さえておくべき勘所
](https://qiita.com/kousaku-maron/items/dfc435936305f266b65b)