18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RDBでGraphQL使いたい

Last updated at Posted at 2018-11-03

はじめに

タイトルの通り、RelationalDatabase(MySQL)でもGraphQL使いたいなぁってなったので普段使ってないけどNodeで書いてみた。

!!ただし読み取りのみ!!

だって書込みは普通にORMのメソッド呼んだ方が楽だから。

環境

  • MySQL 5.7
  • Node 10.11
  • NPM 6.4

成果物 https://github.com/lightstaff/graphql-with-sequelize

準備

適当にnpm init等でプロジェクトを作って必要なライブラリをインストールします。

$: npm install --save apollo-server dataloader-sequelize graphql mysql2 sequelize
  • apollo-server...GraphQLサーバーを簡単に立ち上げてくれる。
  • graphql...主役
  • sequelize...ORMライブラリ
  • dataloader-sequelize...N+1を解決してくれるらしい
  • mysql2...MySQLドライバ

RDB側

ORMにSequelizeを使用しますが、公式見ながら初めて使ってるレベルなので詳細な解説はしません。詳しいことははSequelizeの公式ドキュメント公式EXAMPLEを見てください。

modelsとディレクトリを作って、その中にUserEMailというモデルを作ります。

User ○--< EMail的な関係です。

models/user.js
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define(
    'user',
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      name: DataTypes.STRING,
    },
    {
      timestamps: false,
      tableName: 'user',
    }
  );

  User.associate = models => {
    User.hasMany(models.email);
  };

  return User;
};
models/email.js
module.exports = (sequelize, DataTypes) => {
  const EMail = sequelize.define(
    'email',
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      address: DataTypes.STRING,
    },
    {
      timestamps: false,
      tableName: 'email',
    }
  );

  EMail.associate = models => {
    EMail.belongsTo(models.user);
  };

  return EMail;
};

これらにアクセスできるようにデータベースも定義します。

database.js
const Sequelize = require('sequelize');

const sequelize = new Sequelize('test', 'root', '1234', {
  dialect: 'mysql',
  host: 'localhost',
  port: 3306,
});

module.exports = sequelize;
models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');

const sequelize = require('../database');

const db = {
  sequelize,
  Sequelize,
};

fs.readdirSync(__dirname)
  .filter(file => path.extname(file) === '.js' && file !== 'index.js')
  .forEach(file => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if ('associate' in db[modelName]) {
    db[modelName].associate(db);
  }
});

module.exports = db;

GraphQL側

やっと本題に辿り着いた。

ApolloServerを利用することでお手軽に構築できるみたいです。

非常に単純なので先にソースから載せてしまいます。

server.js
const apolloServer = require('apollo-server');
const dataLoaderSequelize = require('dataloader-sequelize');

const db = require('./models');

const { ApolloServer, gql } = apolloServer;
const { createContext, EXPECTED_OPTIONS_KEY } = dataLoaderSequelize;

// GraphQL schema
const typeDefs = gql`
  type User {
    id: ID!
    name: String
    emails: [Email!]!
  }

  type Email {
    id: ID!
    address: String
    user: User!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
  }
`;

// GraphQL <-> ORM
const resolvers = {
  User: {
    emails: (parent, args, context, info) => parent.getEmails(),
  },
  Email: {
    user: (parent, args, context, info) => parent.getUser(),
  },
  Query: {
    users: (parent, args, { db, eok }, info) =>
      db.user.findAll({ [EXPECTED_OPTIONS_KEY]: eok }),
    user: (parent, { id }, { db, eok }, info) =>
      db.user.findById(id, { [EXPECTED_OPTIONS_KEY]: eok }),
  },
};

// Apollo server
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: {
    db: db,
    eok: createContext(db.sequelize),
  },
});

// Entry point
async function start() {
  await db.sequelize.sync({ force: true });

  // Setup dummy
  const userA = await db.user.create({ name: 'A' });
  const userB = await db.user.create({ name: 'B' });
  await userA.createEmail({ address: 'A1@e-mail.com' });
  await userA.createEmail({ address: 'A2@e-mail.com' });
  await userB.createEmail({ address: 'B1@e-mail.com' });

  server.listen().then(({ url }) => {
    console.log(`🚀 Server ready at ${url}`);
  });
}

start();

非常に単純なので解説するほどでもないのですが・・・。

const typeDefs = ...でGraphQLのスキーマを定義しています。

const resolvers = ...でGraphQlのスキーマに対してデータベースを呼び出して解を定義しています。

const server = ...でApolloServerを作成し、contextにデータベース等を与えています。

async function start()はエントリポイントです。サーバー起動前にデータベースの初期化とデータの追加を行っています。

実行

$ node server.js

で実行です。

image.png

問題なければ上記のようにGraphQLが構築できているはずです。

おわりに

割と簡単にできて良かったと思います。

18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?