💨

serverless-esbuildでgraphqlのファイルを読み込む

2022/01/14に公開

serverless-webpackからserverless-esbuildに乗り換えたときに、graphqlのファイルの読み込みに苦戦したのでメモです。

やりたいこと

apollo serverに渡すgraphqlの定義をgraphqlのファイルを直接読み込みできるようにしたい

src/hander.ts
import typeDef from './schema.graphql'

// 省略

const server = new ApolloServer({
  typeDef,
  resolvers
 });
 
// 省略 

読み込む対象のgraphqlは以下のようなイメージです

schema.graphql
type Query {
  dummy: Dummy!
}

type Dummy {
  hoge: String
}

webpackの場合

webpackではリンクのような方法を利用することで、直接graphqlのファイルをimportできるようになります。
https://www.apollographql.com/docs/react/integrations/webpack/

graphqlの場合

色々調べたところ、読み込みの方法としては以下の3つがありそうです

  • loaderに定義する
  • デプロイ対象のファイルとして、動的に読み込みする
  • pluginを定義する

loaderに定義する

pluginを書いた後に気が付いたのですが、この方法が一番簡単でserverless-esbuildでも紹介されていました。

https://github.com/floydspace/serverless-esbuild/issues/102

やることとしては、serverless.tsのesbuildのオプションに定義するだけです。

serverless.ts
const serverlessConfiguration = {
  custom: {
    esbuild: {
      loader: {
        '.graphql': 'text'
    }
  },
  // 省略
}

デプロイ対象のファイルとして、動的に読み込みする

上記のloaderに定義する場合のリンクで質問者が解決方法としてあげていた方法です。
loaderに定義する方法では、単一のファイルのみの読み込みとなるため、複数のファイルを読み込みする場合等に便利なようです(未検証)

serverless.ts
const serverlessConfiguration = {
  package: {
    include: ['./schema.graphql']
  }
  // 省略
}

この場合、直接importするのではなく、ファイルから読み込みする処理が必要になります

src/handler.ts
import { loadTypedefsSync } from '@graphql-tools/load';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';

// returnは配列なので、最初のデータを取得する
const typeDef = loadTypedefsSync('./schema.graphql', {
  loaders: [new GraphQLFileLoader()]
})[0].document;

pluginを定義する

最初に試した方法です。graphqlのファイルを読み込むesbuildのプラグインを作成しました。

graphqlのファイルを読み込みするプラグインはすでに存在しています。
https://github.com/luckycatfactory/esbuild-graphql-loader

ファイルの読み込み自体はそんなに難しくなさそうだったので、自作してみることにしてみました。
serverless-esbuildのプラグインの利用方法を見ると、以下のようにonResolveの時にフィルターをセットすればよさそうです。
https://github.com/floydspace/serverless-esbuild/blob/master/examples/individually/plugins.js

最終的に、以下のように定義しました

src/plugins.js
const { loadTypedefsSync } = require('@graphql-tools/load');
const { GraphQLFileLoader } = require('@graphql-tools/graphql-file-loader');

let graphqlPlugin = {
  name: 'graphql-loader',
  setup(build) {
    build.onLoad({ filter: /.graphql$/ }, (args) => {
      const typeDefs = loadTypedefsSync(args.path, {
        loaders: [new GraphQLFileLoader()]
      })[0].document;
      return {
        // contentsはstringである必要がある
        contents: JSON.stringify(typeDefs),
        loader: 'json',
      }
    })
  },
}

module.exports = [graphqlPlugin];

serverless.ts側でpluginの読み込みの設定をすればOKです。

serverless.ts
const serverlessConfiguration = {
  custom: {
    esbuild: {
      plugins: 'src/plugins.js',
    }
  },
  // 省略
}

Discussion