Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
14
Help us understand the problem. What is going on with this article?
@ut0n

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

More than 1 year has passed since last update.

はじめに

この記事は 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 を体験してみてください!!!

以上

14
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ut0n

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
14
Help us understand the problem. What is going on with this article?