Contentfulでnullのフィールドにクエリするとエラーが出る問題に Gatsby の createSchemaCustomization で対処する

2023.01.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

どうも、ベルリンオフィスの小西です。

ヘッドレス CMS の Contentful の既知の問題として、いずれの記事でも入力されていない(= null の)フィールドがあった場合、API で記事データを取得しようとすると、そのフィールドが存在すること自体が GraphQL スキーマに出力されない問題があります。

Contentful Plugin: Should output complete schema regardless of whether content is available

事象の再現方法

例えば Contentful で、既存コンテンツモデル sampleContentModel に対し text1 と text2 という テキスト入力フィールドを新規追加します。その後いずれかの記事の text1 だけ入力して publish してみます。

この時点では、当然どの sampleContentModel 記事にも text2 フィールドは入力されておらず、値は null の状態です。

ここでサイト側から GraphQL で記事データをクエリしてみると、text1 のノードしかまだ存在しません。

あまり問題はないように見受けられますが、例えばサイト側の機能拡張を準備していて Contentful 側にフィールドだけ用意しておき、コード側でクエリを書き始めたいみたいなケースでこの問題が邪魔になってきます。

Contentful 側の仕様

現状 gatsby-source-contentful でオフィシャルな解決策はありません。

もちろん Contentful 側で値を入れることで回避は可能ではあるため、ダミーデータを入れておき、フロント側ではそれを迂回してレンダリングする手法などが考えられます。

ただこれでは無駄な処理が増えることになり、運用カバーも必要になるため、あまりいい案ではないかなと思っています(なお Contentful で初期値入力機能を設定しても解決しません)。

対策 on Gatsby

色々調べたところ、「gatsby-node.jscreateSchemaCustomization モジュールのアクション createTypes を利用して、特定のフィールドにデフォルト値を指定する」が個人的ベストな解決策ということになりました。

※なお createSchemaCustomization は Gatsby v2.12 以降で利用可能です。

コード側の処理

gatsby-node.js

exports.createSchemaCustomization = ({ actions, schema }) => {
  const { createTypes } = actions
  let typeDefs = [
    schema.buildObjectType({
      name: "sampleContentModel",
      fields: {
        //text1: "String!",
        text2: {
          type: "String!",
          resolve() {
            return "Default value here!"
          },
        },
      },
      interfaces: ["Node"],
    }),
  ]
  createTypes(typeDefs)
}

exports.createPages = ({ graphql, actions }) => {
...以下略

text2 フィールドが string 型 であることを定義して、ソースを取り込む際の初期値 "Default value here!" を入れています。

こうすることで、フロント側で text1, tex2 に対してクエリを書いてもエラーが出ることがなくなります。

createSchemaCustomization について

Gatsby の GraphQL スキーマをカスタマイズするモジュールです。

下記3つのアクションが利用できます。

  • createTypes … 型の定義、型推論の回避など
  • createFieldExtension … GraphQL スキーマへフィールド拡張の追加。extend 関数で定義された動的な処理をフィールドに追加できる。
  • addThirdPartySchema … サードパーティスキーマの追加

Gatsbyのデフォルトの挙動としては、ソース取り込みのノード生成の際、それぞれの型を推論してくれることになっていますが、明示的に型指定を行なったり、今回のようにデフォルト値を指定できたりします。

データソースのすべてのフィールドについて指定する必要はなく、 gatsby-source-contentful  を通じて流し込まれる他のデータは引き続き Gatsby が型推論してくれます。

余談ですが、Contentfulからのデータソース取り込み時にも各 Contentful フィールドを明示的に型指定するとビルド速くなるのか?と思い Contentful 側に聞いてみたのですが、「わからんが、そうかも」という返答でした。

さいごに

上記以外でも「もっと簡単に解消できるよ」という方がいればぜひ教えてください。

クラスメソッドでは Contentful, Jamstack のご相談、運用支援を承っています。

ご興味のある方はぜひお問い合わせください。

参考資料