LoginSignup
7
3

More than 3 years have passed since last update.

華麗なるGatsby.jsの実践(型を変更してみよう)

Last updated at Posted at 2020-05-18

GraphQLスキーマをカスタマイズする

先日、markdownのimgをgraphQLで管理しようとしたところ、型の類推エラーによってgraphQLのスキーマをいじらなければならなくなりました。なのでこのガイドを学習します!
ハイパー意訳がほぼほぼですのでご了承ください🙇‍♂️

graphQLのスキーマ言語をおおよそ知る必要がある

その前に、そもそも、graphQLに関する記述法を知らなかったので、そこをざっくり学習します。
https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000
私は上記で学習させていただきました!

特に必要となる知識は、スキーマ言語(Web APIの仕様を定義する方)の
fieldの定義方法と,Interfaceについてです。

type Query {
 currentUser: User!
}
interface Node {
 id: ID!
}

Interfaceは必要となるfieldのセットを束ねた抽象型です。

公式ガイド

では公式ガイドを見てみます。

このガイドは

  • プラグインの作成者

  • 自動型類推によって作成されたスキーマを修正したい方(私)

  • ビルドの最適化を測る方

  • スキーマに興味のある方

を対象としているようです。こちらかなり長いのですが、ここでは前編部分のみを扱います。
気になる方は続きを読むと良さそうです!

graphQLの強みは何と言っても多くの情報源を一括で扱えること!
そのためにはGraphQLスキーマを生成しなければなりません。

例 マークダウンとJSON

Markdownファイルからブログ記事を生成する。記事内容に加えて筆者情報をJSON形式で提供する。
時々ある寄稿者の情報は別のJSONとして保存する。

.md
---
title: Sample Post
publishedAt: 2019-04-01
author: jane@example.com
tags:
  - wow
---
# Heading
Text
author.json
[
  {
    "name": "Doe",
    "firstName": "Jane",
    "email": "jane@example.com",
    "joinedAt": "2018-01-01"
  }
]
contributor.json
[
  {
    "name": "Doe",
    "firstName": "Zoe",
    "email": "zoe@example.com",
    "receivedSwag": true
  }
]

これらをGraphQLでクエリするためにはgatsby-source-filesystem,gatsby-transformer-remark,gatsby-transformer-jsonを使用しますが、これらのプラグイン内部で何が行われているかというと、
markdownファイルをユニークなidとMarkdownRemarkタイプを持つnodeオブジェクトに変換しています。

同様に著者データはAuthorJsonタイプのnodeオブジェクトに変換され、
寄稿者データはContributorJson型のノードオブジェクトに変換されています。

スクリーンショット 2020-05-18 18.57.49.png

Node interface

Source pluginやtransformer pluginによって作られるGatsbyのGraphQLのスキーマです。
Id, parent,children,またinternal型などといったfieldがセットされています。

interface Node {
  id: ID!
  parent: Node!
  children: [Node!]!
  internal: Internal!
}

type Internal {
  type: String!
}

** TODO: internalって何でしょう..イマイチ調べてもわからずでした。

例 autor.json

例)Gatsby-transformer-jsonで作成するautor.json用のnode型は、以下のようになっています。

type AuthorJson implements Node {
  id: ID!
  parent: Node!
  children: [Node!]!
  internal: Internal!
  name: String
  firstName: String
  email: String
  joinedAt: Date
}

実際にgatsbyが作成したスキーマを確認するには、GraphQL Playgroundがオススメです。

プロジェクト上で

$ GATSBY_GRAPHQL_IDE=playground gatsby develop

そしてhttp://localhost:8000/___graphqlを開いて右画面側にある Schemeタブを開きます。
スクリーンショット 2020-05-18 18.03.52.png

思った以上にぎっしりしてます。

自動型推論

先ほどのautor.jsonを見てみましょう。

[
  {
    "name": "Doe",
    "firstName": "Jane",
    "email": "jane@example.com",
    "joinedAt": "2018-01-01"
  }
]

どこにも型定義などされていないですが、GraphQLではうまいこと動かせています。
これは型の類推を行なってくれているからですね。
1つ1つのfield内容を確認し、型をチェックすることで実現しています。

しかしこの型類推には2つの問題点があります。

  • 時間がかかり、負担がかかる

  • 型とデータが異なっていた場合、型の類推が失敗する

2つ目についてですが、例をあげます。

.md
  +  {
  +    "name": "Doe",
  +    "firstName": "John",
  +    "email": "john@example.com",
  +    "joinedAt": "201-04-02"
  +  }
  ]

joinedAt部分がスペルミスによって、Dateと解釈できなくなっています。

これらを解決するためには、型を明示的に示すことです。

## 型定義

createType actionを使って型を明示的に定義します。

gatsby-node.js
  exports.createSchemaCustomization = ({ actions }) => {
    const { createTypes } = actions
    const typeDefs = `
      type AuthorJson implements Node {
        joinedAt: Date
      }
    `
    createTypes(typeDefs)
  }

全ての型を定義する必要がない点に注意してください。(name,firstName等)

そもそも型推論をやらない!というストイックな方もいるでしょう。
これを行うことでパフォーマンスの向上が見込めます。先ほども開設したように、型の類推は負担が大きいため、大規模なプロジェクトほどパフォーマンス低下が著しくなります。
@dontInferディレクティブを用いて型推論をオプトアウトできます。

ディレクティブってなんだ?という方は先ほどのGraphQLスキーマについての解説をどうぞ!https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000)

gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    type AuthorJson implements Node @dontInfer {
      name: String!
      firstName: String!
      email: String!
      joinedAt: Date
    }
  `
  createTypes(typeDefs)
}

これがストイックなバージョンになります。
Node interfaceが提供するフィールドについては、Gatsby側で追加してくれるので明示しなくて大丈夫です。(id,parentなど)

ネストしている型

今まではString型やDate型などのスカラー型のみを扱ってきました。GraphQLでは他にも

  • ID型

  • Int型

  • Float型

  • Boolean型

  • JSON型

なども扱うことが可能です。また、複雑なオブジェクト値だって扱えます。

markdown-remarkを例にとってみます。

以下の記述によってfrontmatter.tagsは必ず文字の配列となります。

gatsby-node.js
  exports.createSchemaCustomization = ({ actions }) => {
    const { createTypes } = actions
    const typeDefs = `
      type MarkdownRemark implements Node {
        frontmatter: Frontmatter
      }
      type Frontmatter {
        tags: [String!]!
      }
    `
    createTypes(typeDefs)
  }

こちらの記述のように、直接Frontmatter型を指定すると失敗してしまいます。

gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    # これは失敗!!!
    type Frontmatter {
      tags: [String]!
    }
  `
  createTypes(typeDefs)
}

なぜなら、Frontmatter型がソースプラグインやトランフォーマープラグインによって作成されないため、Nodeインターフェースが実装されくなってしまうからです。

そのためにこのような処理となります。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3