Closed6

koa と Apollo Server V4 で GraphQL サーバ構築

siropacasiropaca

業務で GraphQL を使いだしたので、練習のために1から GraphQL サーバを構築してみる。
Apollo Server の4系が先月リリースされたっぽいのでそちらを使用。

リリースノート

https://github.com/apollographql/apollo-server/releases/tag/%40apollo%2Fserver%404.0.0

サーバーサイドのライブラリは何でもよかったが、軽量かつコミニティで4系のサポートをしている koa を採用してみる。

express は公式でミドルウェアをサポートしてるらしい。
https://www.apollographql.com/docs/apollo-server/api/express-middleware/

他のコミニティサポートはここで確認できる。
https://www.apollographql.com/docs/apollo-server/integrations/integration-index/

siropacasiropaca

まずは、必要最低限のライブラリをインストールしてみる。

$ yarn add koa ts-node typescript
$ yarn add -D @types/koa @types/node
siropacasiropaca

ディレクトリ構成は以下の通り。
(実際はモノリポ構成で同階層に frontend/ もある)

backend/
├── README.md
├── node_modules/
│   └── ...
├── .gitignore
├── package.json
├── src/
│   └── server.ts
├── tsconfig.json
└── yarn.lock
siropacasiropaca

例によって Hello world 的なことをする。

koa の README に記載してある、Hello Koa を参考にしつつ、src/server.ts を実装。

https://github.com/koajs/koa#hello-koa

// src/index.ts
import Koa from "koa";

const app = new Koa();

app.use((ctx) => {
  ctx.body = "Hello Koa!";
});

app.listen(3000, () => {
  console.log("started server on http://localhost:3000");
});

npm scripts に "dev": "ts-node src/server.ts" を追加して、実行してみる。

$ yarn dev
yarn run v1.22.19
> ts-node src/server.ts
started server on http://localhost:3000

3000 番ポートにアクセスできることを確認 👀

siropacasiropaca

@as-integrations/koa を参考に @apollo/serverkoa に組み込んでみる。

https://www.apollographql.com/docs/apollo-server/integrations/integration-index/

足りてないライブラリをインストール。

$ yarn add koa-bodyparser @koa/cors @apollo/server graphql @as-integrations/koa
$ yarn add -D @types/koa-bodyparser @types/koa__cors

@as-integrations/koa のサンプルは .mjs だが .ts でやりたいので、即時関数で囲って実行してみたら実行はできた。
型が合わなかったので、() => resolve(null) にしてみたけどこれはいいのかは不明。

// server.ts
import http from 'http';
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import cors from '@koa/cors';
import { ApolloServer } from '@apollo/server';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import { koaMiddleware } from '@as-integrations/koa';

// The GraphQL schema
const typeDefs = `#graphql
type Query {
    hello: String
}
`;

// A map of functions which return data for the schema.
const resolvers = {
  Query: {
    hello: () => "world",
  },
};

(async () => {
  const app = new Koa();
  const httpServer = http.createServer(app.callback());

  // Set up Apollo Server
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  });
  await server.start();

  app.use(cors());
  app.use(bodyParser());
  app.use(
    koaMiddleware(server, {
      context: async ({ ctx }) => ({ token: ctx.headers.token }),
    })
  );

  await new Promise((resolve) =>
    httpServer.listen({ port: 3000 }, () => resolve(null))
  );
  console.log(`🚀 Server ready at http://localhost:3000`);
})();
$ yarn dev
yarn run v1.22.19
> ts-node src/server.ts
🚀 Server ready at http://localhost:3000

siropacasiropaca

Koa.js でルーティングを行うには、Router が別途必要。

https://github.com/koajs/router

$ yarn add @koa/router @types/koa__router

入れたら、あとはルーティングの記述を追加するだけ。

import Router from '@koa/router';

// 略

app.use(cors());
app.use(bodyParser());

router.post(
  '/graphql',
  koaMiddleware(server, {
    context: async ({ ctx }) => ({ token: ctx.headers.token }),
  }),
);

app.use(router.routes());
app.use(router.allowedMethods());

await new Promise((resolve) =>
    httpServer.listen({ port: 3000 }, () => resolve(null))
);
console.log(`🚀 Server ready at http://localhost:3000`);

これで、http://localhost:3000/graphql がエンドポイントになる。

このスクラップは5ヶ月前にクローズされました