なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

Cloudflare Workers で GraphQL バックエンドを快適に開発したい

Cloudflare Workers で GraphQL バックエンドを構築する際、最初はサンプルなどから簡単に始められますが、実際の開発では「スキーマをコード内に書きたくない」「型安全性が欲しい」といった課題に直面します。 この記事では、Cloudflare Workers の制約の中で、これらの課題を解決し、快適な GraphQL 開発環境を構築する方法を紹介します。

まずは、シンプルな hello クエリを解決するコードを見てみます。

import { ApolloServer } from '@apollo/server';
import { startServerAndCreateCloudflareWorkersHandler } from '@as-integrations/cloudflare-workers';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';

const typeDefs = /* graphql */`
  type Query {
    hello: String!
  }
`;

const resolvers = {
  Query: {
    hello: () => {
      return 'Hello World!';
    },
  }
}

export interface Context {
}

const server = new ApolloServer<Context>({
  typeDefs,
  resolvers,
  introspection: true,
  plugins: [
    ApolloServerPluginLandingPageLocalDefault({ footer: false }),
  ],
});

export interface Env {
  // ...
}

export default {
  fetch: startServerAndCreateCloudflareWorkersHandler<Env, Context>(server, {
    context: async ({ env, request, ctx }) => {
      return { };
    },
  }),
};

この実装は動作しますが、以下の問題点があります:

  1. スキーマがインラインで書かれている - コードが読みにくく、メンテナンスしづらい、また index.ts が肥大化する
  2. Resolverに型が付いていない - 型安全性が担保されず、バグの温床になりやすい

このような問題点を解決するため、それぞれ以下の解決法を試しました。

解決策1: スキーマをファイルに分離する

まず、スキーマを別ファイル schema.graphql として切り出します。

type Query {
  hello: String!
}

次に、wrangler.jsoncrules 項目を追加します。これにより、ビルド時に静的ファイルを任意の形式として解決できるようになります。

{
   // ... 他の設定 ...
  "rules": [
    {
      "type": "Text",
      "globs": ["**/*.graphql"],
      "fallthrough": false
    }
  ]
}

この設定により、*.graphql ファイルを文字列としてインポートできるようになります。 その結果、コードは次のような形に変更できます。

// @index.ts
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateCloudflareWorkersHandler } from '@as-integrations/cloudflare-workers';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
import Schema from "./schema.graphql"; // インポートする形に

const resolvers = {
  Query: {
    hello: () => {
      return 'Hello World!';
    },
  }
}

export interface Context {
}

const server = new ApolloServer<Context>({
  typeDefs: Schema,
  resolvers,
  introspection: true,
  plugins: [
    ApolloServerPluginLandingPageLocalDefault({ footer: false }),
  ],
});

export interface Env {
  // ...
}

export default {
  fetch: startServerAndCreateCloudflareWorkersHandler<Env, Context>(server, {
    context: async ({ env, request, ctx }) => {
      return { };
    },
  }),
};

// @globals.d.ts
declare module "*.graphql" {
  declare const content: string;
  export default content;
}

globals.d.ts (名前はなんでも良い) の型定義だけ、追加を忘れないようにしましょう。

解決策2: GraphQL Code Generator で型を自動生成する

GraphQL Code Generator とその関連パッケージをインストールします。

$ pnpm i @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers --save-dev

次に Code Generator の設定ファイルを生成します。2025年10月時点では、設定ファイルは TypeScript 形式で生成されます。

$ npx graphql-code-generator init

生成された codegen.ts を編集します。

import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  overwrite: true,
  schema: "./src/schema.graphql", // スキーマファイルのパス
  generates: {
    "src/generated/graphql.ts": {
      plugins: ["typescript", "typescript-resolvers"],
      config: {
        contextType: "../index#Context", // Contextの型を指定。今回の場合は `index.ts` の `Context`
      },
    },
  },
};

export default config;

最後に、以下のコマンドで型定義を生成します。

$ pnpm run codegen

最終的なコード

生成された型定義をインポートして、型安全な Resolver を実装します。

import { ApolloServer } from '@apollo/server';
import { startServerAndCreateCloudflareWorkersHandler } from '@as-integrations/cloudflare-workers';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
import Schema from "./schema.graphql";
import type { Resolvers } from "./generated/graphql";

const resolvers: Resolvers = {
  Query: {
    hello: () => {
      return 'Hello World!';
    },
  }
}

export interface Context {
}

const server = new ApolloServer<Context>({
  typeDefs: Schema,
  resolvers,
  introspection: true,
  plugins: [
    ApolloServerPluginLandingPageLocalDefault({ footer: false }),
  ],
});

export interface Env {
  // ...
}

export default {
  fetch: startServerAndCreateCloudflareWorkersHandler<Env, Context>(server, {
    context: async ({ env, request, ctx }) => {
      return { };
    },
  }),
};

まとめ

Cloudflare Workers で快適に GraphQL 開発を行うためのポイントは以下の2つです:

  1. rules を追加して、スキーマを別ファイルに分離する - コードの可読性とメンテナンス性が向上
  2. graphql-codegen を使って型定義を自動生成する - 型安全性が担保され、開発体験が大幅に改善

これらの設定により、Cloudflare Workers でも本格的な GraphQL バックエンド開発が可能になります。スキーマの変更時には pnpm run codegen (追加されているはず) を実行するだけで、自動的に型が更新されるため、効率的な開発が実現できます。

ということで、最近やったことで困ったことの記事でした。それではまた。


参考リンク