Express API 서버에 GraphQL 끼얹기 3편 - GraphQL-tools로 Schema 만들기

이진선·2019년 12월 2일
0

{ 3. GraphQL-tools로 Schema 만들기 }

GraphQL-tools 란?

A set of utilities to build your JavaScript GraphQL schema in a concise and powerful way.
파워풀하게 우리를 도와준다고 한다.

바로 코드로 들어가 보자.

const typeDefs = `
  type Author {
    id: Int!
    firstName: String
    lastName: String
    """
    the list of Posts by this author
    """
    posts: [Post]
  }

  type Post {
    id: Int!
    title: String
    author: Author
    votes: Int
  }

  # the schema allows the following query:
  type Query {
    posts: [Post]
    author(id: Int!): Author
  }

  # this schema allows the following mutation:
  type Mutation {
    upvotePost (
      postId: Int!
    ): Post
  }
`;
import { find, filter } from 'lodash';

// example data
const authors = [
  { id: 1, firstName: 'Tom', lastName: 'Coleman' },
  { id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
  { id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
];

const posts = [
  { id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },
  { id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 },
  { id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },
  { id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 },
];

const resolvers = {
  Query: {
    posts: () => posts,
    author: (_, { id }) => find(authors, { id }),
  },

  Mutation: {
    upvotePost: (_, { postId }) => {
      const post = find(posts, { id: postId });
      if (!post) {
        throw new Error(`Couldn't find post with id ${postId}`);
      }
      post.votes += 1;
      return post;
    },
  },

  Author: {
    posts: author => filter(posts, { authorId: author.id }),
  },

  Post: {
    author: post => find(authors, { id: post.authorId }),
  },
};
import { makeExecutableSchema } from 'graphql-tools';

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

위 코드는 공식 사이트에서 제공하는 기본 코드이다.
조금 더 간단해진 느낌이다.
typeDefsm resolver를 각각 작성해서 makeExecutableSchema 해주면 된다.

우리는 다만 또다시 모듈화를 어떻게 할 것인지에 대한 고민을 해야한다. 다수의 테이블이 있으므로, 테이블 각각마다 Type이 있을테고, 그에 속하는 다양한 Query, Mutation이 존재할 것이므로 이를 어떻게 이쁘게 모듈화를 할것인지에 대한 고민이 선행되어야한다.

나는 이렇게 정했다.

gql 이라는 루트 디렉토리가 있고,
이 안에 테이블이름을 디렉토리 이름으로 가지는 다수의 서브디렉토리를 둔다.
서브디렉토리는 각각 type.gql, query.js, mutation.js 파일을 가지고. 이 파일들은 모든 서브디렉토리에 반드시 존재한다.
루트 디렉토리에 index.js 에서 서브 디렉토리를 모두 읽어서 하나의 type, query, mutation으로 합성하여
typeDefs, resolver를 만들어 내고, makeExecutalbeSchema하여 exports 한다.

이해를 돕기위해 코드와 디렉토리를 보자.

먼저 gql 이름에 루트 디렉토리가 있고
그 안에 다수의 서브디렉토리가 있다.

이 서브 디렉토리들은 각각
type.gql
query.js
mutation.js
파일을 필수로 가진다

user/type.gql

type User {
    id: Int!
    user_name: String
    team: Team!
}
extend type Query  {
    users: [User]
}
extend type Mutation  {
    userEdit(id: Int!, user_name: String): User
}

user/query.js

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

async function users(root, args, context, info) {
    console.log('users');
    return await models.User.findAll();
}
module.exports = {
    users,
}

user/mutation.js

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

async function userEdit(root, args, context, info) {
    console.log('user_name Edit2');
    console.log(args)
    await (models.User.update({user_name: args.user_name}, {
        where: {id: args.id},
    }));
    return await models.User.findOne({where: {id: args.id}});
}
module.exports = {
    userEdit,
}
;

세종류의 파일은 모두 gql/index.js 파일에서 읽힌다

const fs = require('fs');
const path = require('path');
const {makeExecutableSchema} = require('graphql-tools');

let typeDefs = '';
fs
    .readdirSync(__dirname)
    .filter((file) => {
        return (file.indexOf('.') === -1);
    })
    .forEach((file) => {
        typeDefs += fs.readFileSync(path.join(__dirname, file, 'type.gql'));
    });

const Query = {};
fs
    .readdirSync(__dirname)
    .filter((file) => {
        return (file.indexOf('.') === -1);
    })
    .forEach((file) => {
        const func = require(path.join(__dirname, file, `query.js`));
        for (const [key, value] of Object.entries(func)) {
            Query[key] = value;
        }
    });

const Mutation = {};
fs
    .readdirSync(__dirname)
    .filter((file) => {
        return (file.indexOf('.') === -1);
    })
    .forEach((file) => {
        const func = require(path.join(__dirname, file, `mutation.js`));
        for (const [key, value] of Object.entries(func)) {
            Mutation[key] = value;
        }
    });

const resolvers = {
    Query,
    Mutation,
};
module.exports = makeExecutableSchema({
    typeDefs,
    resolvers,
});

나는 위와 같은 방식으로 모듈화하여 접근하였다.
추가로 작성하고 싶은 테이블이 있으면 디렉토리를 만들고, 세가지 파일을 추가해주면 된다.

추가하고 싶은 쿼리나 뮤테이션은 해당 파일에 새로 정의하여 주면 되고,

0개의 댓글