はじめに
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
ES6でJavascriptを書けるようにする
まず、neo4j-graphql-sampleフォルダを作成します。
import等、ES6のJavascriptをNodejsで実行できるコードへトランスパイルするためにbabelを導入します。
詳しくはこちらの記事見てください。
$ mkdir neo4j-graphql-sample
$ cd neo4j-graphql-sample
$ npm init -y
$ npm install --save-dev babel-cli babel-preset-env
.babelrcを作成します。
{
"presets": [
[
"env", {
"targets": {
"node": "current"
}
}
]
]
}
package.jsonを修正します。
{
...
"scripts": {
"start": "babel-node index.js"
},
...
}
GraphQL実装に必要なライブラリをインストール
GraphQL実装には、Apolloというライブラリを使用します。
Apolloを使用すると簡単にGraphQLサーバーとフロントエンドをつなぐことができます。GraphQL実装の話はもはやApolloサーバー実装の話になっているほどメジャーなライブラリです。
また、neo4jを弄り回すので、neo4j-driver
とneo4j-graphql-js
を利用します。
$ 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を作成し実際にコードを書いていきます。
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の型を作ります。
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サーバーを動かしてみましょう。
$ 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のクエリを基礎から整理してみた
createUserのMutationでUserを追加してみましょう。
writeArticleのMutationでArticleを追加してみましょう。
createUser実行時取得したUserのuuidをuser_uuidに入れて実行しましょう。
ArticleのQueryで参照してみましょう。
ちなみに、下記のようなクエリを流すとArticleを書いたUserの情報も参照することが可能です。
現時点でNeo4jにはノードとして「User」、「Article」、エッジとして「WRITE」が登録されているはずです。ブラウザでNeo4jエディタにアクセスし、確認してみましょう。
さいごに
「User」と「Article」を追加、参照するGraphQLを作ることができました。
サンプルコードgithubにあげておきました。
https://github.com/kousaku-maron/neo4j-graphql-sample
今回はMutationを独自で組み込みましたが、エッジの「WRITE」を自動でつける必要がないのであれば、makeAugmentedSchema
のconfig
のmutation
をtrueにすれば、独自で作らなくても勝手にMutationが使えるようになります。
ここでは解説していない認証・認可や、GraphQLを実装する際に考えるべき点をまとめた記事も書いてます。よかったらのぞいてみてください。