25
19

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.

GraphQLAdvent Calendar 2019

Day 8

express-graphql + TypeScript で始めるGraphQL API Server

Last updated at Posted at 2019-12-08

はじめに

この記事は express-graphqlNode.js + TypeScript で簡単にGraphQL APIサーバを実装する
ハンズオンちっくな記事です。
実際に手を動かしてみてください🙏

ディレクトリ構造は下記のようになります。

.
├── src
│   ├── data
│   │   └── index.ts
│   ├── fields
│   │   ├── index.ts
│   │   └── member
│   │       ├── index.ts
│   │       ├── query.ts
│   │       ├── mutation.ts
│   │       ├── resolvers.ts
│   │       └── types.ts
│   └── index.ts
├── package.json
└── tsconfig.json

準備

パッケージのインストール

実行は ts-node で行います。

yarn add @types/express cors express express-graphql graphql typescript
yarn add -D ts-node tsconfig-paths

tsconfig.json

alias の登録をします。

tsconfig.json
{
  "compilerOptions": {
    "sourceMap": false,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "lib": ["es2018", "dom"],
    "moduleResolution": "node",
    "removeComments": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "strictFunctionTypes": false,
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"],
    }
  },
  "include": [
    "./src/**/*.ts"
  ]
}

GraphQL Query

メンバー一覧を取得するAPI を実装します。

実装

Data

実際にこのDataは SQL などからDBの値を取得しますが、今回は DB との接続はなしで JS で用意します。

src/data/index.ts
export const memberList = [
  {
    id: 1,
    name: 'Rachel',
    age: 29
  },
  {
    id: 2,
    name: 'Ross',
    age: 29
  },
  {
    id: 3,
    name: 'Joey',
    age: 29
  }
];  

Types

型定義を types.ts として作成します。

src/fields/member/types.ts
import { GraphQLObjectType, GraphQLNonNull, GraphQLString, GraphQLInt } from 'graphql';

export const memberType = new GraphQLObjectType({
  name: 'member',
  description: 'member',
  fields: {
    id: {
      type: new GraphQLNonNull(GraphQLInt),
      description: 'The Member ID.'
    },
    name: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'The Member name.'
    },
    age: {
      type: new GraphQLNonNull(GraphQLInt),
      description: 'The Member age.'
    }
  }
});

Resolvers

Resolver では何をレスポンスするかの処理を書きます。
この例ではメンバーリストを取得したいだけなので、そのまま memberList を返します。

src/fields/member/resolvers.ts
import { memberList } from '@/data';

export const getMemberList = () => Promise.resolve(memberList);

Query

Query は REST APIの GET に相当します。

src/fields/member/query
import { GraphQLList } from 'graphql';
import { getMemberList } from '@/fields/member/resolvers';
import { memberType } from '@/fields/member/types';

export const memberQuery = {
  memberList: {
    type: new GraphQLList(memberType),
    description: 'Get list of members data.',
    resolve: getMemberList
  }
};

src/fieles/member/index.ts

実装した member モジュールの query をまとめてエクスポートします。

src/fields/member/index.ts
import { memberQuery as query } from '@/fields/member/query';

export const memberField = {
  query
};

src/fields/index

実装したすべてのモジュールを Root Query としてまとめてエクスポートします。

src/fields/index.ts
import { GraphQLObjectType } from 'graphql';
import { memberField } from '@/fields/member/';

export const queryType = new GraphQLObjectType({
  name: 'Query',
  description: 'The root query type.',
  fields: {
    ...memberField.query
  }
});

Express

最後に Express でサーバを実装します。

src/index.ts
import * as express from 'express';
import * as graphqlHTTP from 'express-graphql';
import { GraphQLSchema } from 'graphql';
import { queryType } from '@/fields/';

const PORT = 4000;
const app = express();

const schema = new GraphQLSchema({
  query: queryType
});

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

app.listen(PORT, () => console.log('Listening on :4000'));

実行

サーバ起動

下記コマンドでAPIサーバを起動します。

yarn ts-node -r tsconfig-paths/register src/index.ts

動作チェック

localhost:4000/graphql にアクセスすると、GraphiQL エディタが起動します。

下記クエリを入力して実行。memberList が取得できれば成功🎉

query getMemberList {
  memberList {
    id
    name
    age
  }
}
スクリーンショット 2019-12-09 02.43.18.png

GraphQL Mutation

続いて Mutation を実装します。
この例では新メンバーを追加する Mutation を実装します。

実装

Types

入力側のパラメータの型を追加します。

src/fields/member/types.ts
export const memberCreateInput = new GraphQLInputObjectType({
  name: 'memberCreateInput',
  fields: {
    name: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'The Member name.'
    },
    age: {
      type: new GraphQLNonNull(GraphQLInt),
      description: 'The Member age.'
    }
  }
});

Resolvers

新メンバーを追加する処理を追加します。

src/fields/member/resolvers.ts
export const createMember = ({ name, age }: { name: string; age: number }) => {
  const member = {
    id: memberList.length + 1,
    name,
    age
  };
  memberList.push(member);
  return memberList;
};

Mutation

新たに Mutation を作成します。

src/fields/member/mutation.ts
import { GraphQLNonNull, GraphQLList } from 'graphql';
import { createMember } from '@/fields/member/resolvers';
import { memberType, memberCreateInput } from '@/fields/member/types';

export const memberMutation = {
  createMember: {
    type: new GraphQLList(memberType),
    args: {
      member: {
        type: new GraphQLNonNull(memberCreateInput)
      }
    },
    resolve: (_: any, args: any) => {
      return createMember(args.member);
    }
  }
};

src/fieles/member/index.ts

実装した member の Mutation をエクスポートします。

src/fields/member/index.ts
import { memberQuery as query } from '@/fields/member/query';
import { memberMutation as mutation } from '@/fields/member/mutation';

export const memberField = {
  query,
  mutation
};

src/fields/index

実装したすべてのモジュールを Root Mutation としてまとめてエクスポートします。

src/fields/index.ts
import { GraphQLObjectType } from 'graphql';
import { memberField } from '@/fields/member/';

export const queryType = new GraphQLObjectType({
  name: 'Query',
  description: 'The root query type.',
  fields: {
    ...memberField.query
  }
});

export const mutationType = new GraphQLObjectType({
  name: 'Mutation',
  description: 'The root Mutation type.',
  fields: {
    ...memberField.mutation
  }
});

Express

最後に SchemaMutation を追加します。

src/index.ts
import { queryType, mutationType } from '@/fields/';

const schema = new GraphQLSchema({
  query: queryType,
  mutation: mutationType
});

実行

サーバ起動

下記コマンドでAPIサーバを起動します。

yarn ts-node -r tsconfig-paths/register src/index.ts

動作チェック

下記クエリを入力して実行。memberList に入力したメンバーが追加できれば成功🎉

mutation createMember {
  createMember(member: {
    name: "Monica"
    age: 29
  }) {
    id
    name
    age
  }
}
スクリーンショット 2019-12-10 17.28.15.png

さいごに

GraphQL のメリットとして、書いたコードがそのままドキュメントになることが挙げられます。
予め description を書くルールなどを定めておけばAPI ドキュメントを用意する必要がなくなります。

実際に手を動かして、GraphQL を体験してみてください!!!

以上

25
19
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
25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?