Help us understand the problem. What is going on with this article?

MTのAPIをGraphQL化してみた(途中)

これは Movable Type Advent Calendar 2019 3日目の記事です。
ちなみにこれを書いているのは12月9日月曜日。何を隠そう、登録したことも登録した日付もすっかり忘れて、ツッコミが入って慌てて書いております。自分で登録しておいて非常に申し訳ない。。。

MTのAPIをGraphQL化してみた(途中)

みなさんはGraphQLというものをご存知ですか??
2015年くらいから公開されたりして、色々世間ではRestAPIに変わる手法として開発したFaceBookや、その他にもGitHub、Airbnb、Netflixなどの大きな企業が採用したことでも話題となっているらしいです。
ちなみに私はへぇ〜便利なものができたのね〜くらいの知識しかなくて、いつか触ってみたいなと思っていたので、今回のAdventCalendarのテーマとしてMTのAPIをGraphQL化してみながら勉強してみました。

ただ、途中と書いてあるのは、そもそもGraphQL自体の理解が浅いのと、MTのAPI全てをカバーするにはとんでもない時間がかかるため、まだまだ入り口のあたりまでとなります。
今後も開発は続けていきますので今回は導入編として生暖かい目でみていただければと思います。
(ツッコミも大歓迎です)

GraphQLってなに??

GraphQLって結局なんなのという方 自分の のためにまとめてみました。
GraphQLというのはAPIの一種です。(ざっくり)

MTのAPIもそうですが、今まではREST(RESTful)と呼ばれる方式で実装されていることが多い(はず)です。

Rest

  • リソース(取得できる結果)ごとにURLがある
  • URLによって得られる結果が決まっている
  • 情報の操作(取得、作成、更新、削除)は全てHTTPメソッド(GET、POST、PUT、DELETE)

MTのDataAPIでコンテンツデータを取得する場合、以下のようになります。
MT ContentData API リファレンス

[GET] http://example.com/mt/mt-data-api.cgi/v4/sites/{site_id}/contentTypes/{content_type_id}/data

そして得られる結果は下記のような形になります。(リファレンスより)

{
  "totalResults": 1,
  "items": [
    {
      "author": {
        "displayName": "Hello, world!",
        "id": 1,
        "userpicUrl": "Hello, world!"
      },
      "basename": "Hello, world!",
      "blog": {
        "id": 1
      },
      "createdDate": "Hello, world!",
      "data": [
        {
          "data": "Hello, world!",
          "id": 1,
          "label": "Hello, world!",
          "type": "Hello, world!"
        }
      ],
      "date": "Hello, world!",
      "id": 1,
      "label": "Hello, world!",
      "modifiedBy": {
        "displayName": "Hello, world!",
        "id": 1,
        "userpicUrl": "Hello, world!"
      },
      "modifiedDate": "Hello, world!",
      "status": "Hello, world!",
      "unpublishedDate": "Hello, world!",
      "updatable": true
    }
  ]
}

管理画面から更新しない限り、上記のURLにアクセスした場合は同じ結果が返ってきます。

特徴としてリソースごとにURLがあるので、上記URLで取得できるのはContentDataの情報のみです。
サイトの名前やコンテンツタイプの名前などを取得したい場合はまた別のURLで取得することになります。

APIを使って動的に情報を取得しながら画面に表示する際、
画面がContentDataのみの情報で足りるのであれば問題にはなりません。

しかし大抵は、どのサイトのコンテンツデータなのかや、どのコンテンツタイプのコンテンツデータなのかという情報が必要になる場合が多いと思います。
その場合は、RestAPIでは複数回に分けてアクセスする必要がありました。

  1. サイト情報を取得する(/sites/{site_id})
  2. コンテンツタイプ情報を取得する(/sites/{site_id}/contentTypes/{content_type_id})
  3. コンテンツデータを取得する(/sites/{site_id}/contentTypes/{content_type_id}/data/{content_data_id})

使う側のユーザもどのエンドポイントでどの結果が返ってくるのかを考えて使う必要があったり、
開発側は取得したい結果が変わるとその都度エンドポイントを増やす必要があります。

またエンドポイントに破壊的な変更(取得できる結果が変わる場合など)には
バージョン番号の/v4//v5などに変更して、
今までのエンドポイントで得られる結果は保証するというのも特徴の一つです。

極端な話、返ってくる結果でフィールドが一つ増減した場合にはバージョン番号を更新します。

こうした理由でエンドポイントがどんどん増えていって、その度にマニュアルを作り直したり、リリースが必要になったり、
すごく手間がかかる作業になってきます。

GraphQL

GraphQLでは上記Restの問題を解決するために下記のような特徴があります。

  • URL(エンドポイント)は一つ
  • 取得したいデータをアクセス時にbodyに送る(POST送信)
  • 情報の操作(取得、作成、更新、削除)はbodyないで書く(Query、Mutation)

GraphQLの勉強のため、MTのAPIの一部をGraphQL化してみました。
わかりやすくシンプルに実装してみました(知識が浅いだけ)

[POST] http://example.com/mt/plugins/GraphQL/graphql.cgi

body

{ 
  sites { 
    name
  }
  contentdata(Filter: {siteID: 1}) {
    id
    label
  }
}

その結果得られるjsonは下記になります。

{
    "data": {
        "contentdata": [
            {
                "id": "48",
                "label": "Now burning pamphlets at this very."
            },
            {
                "id": "49",
                "label": "That they were awake was the."
            },
            {
                "id": "50",
                "label": "Now hanging a fair faces trod!"
            }
        ],
        "sites": [
            {
                "name": "test"
            },
            {
                "name": "test1"
            }
        ]
    }
}

結果の部分でdataの中に「contentdata」と「sites」があります。
「contentdata」がコンテンツデータの情報、「sites」がサイトの情報となっており、
コンテンツデータとサイトの情報が1つのリクエストで取得することができます。

次はサイト全てとそのサイトに含まれているコンテンツデータを取得してみます。

[POST] http://example.com/mt/plugins/GraphQL/graphql.cgi

body

{ 
  sites { 
    name
    contentdata {
      label
    }
  }
}

その結果得られるjsonは下記になります。


{
    "data": {
        "sites": [
            {
                "contentdata": [
                    {
                        "label": "Now burning pamphlets at this very."
                    },
                    {
                        "label": "That they were awake was the."
                    },
                    {
                        "label": "Now hanging a fair faces trod!"
                    }
                ],
                "name": "test"
            },
            {
                "contentdata": [
                    {
                        "label": "test"
                    },
                    {
                        "label": "test"
                    }
                ],
                "name": "test1"
            }
        ]
    }
}

このjsonを使えばサイトごとのコンテンツデータの一覧を表示するなどができそうです。

お気づきかもしれませんが、実は返ってくる結果の中で、1つめのサンプルでコンテンツデータはidlabelが、
2つめはlabelのみが返ってきます。

特徴の2つめにある取得したいデータをアクセス時にbodyに送るという部分がここです。

1つめは

{ 
  sites { 
    name
  }
  contentdata(Filter: {siteID: 1}) {
    id
    label
  }
}

contentdataの中にidlabelを書いているので、結果もidlabelが返ってきます。

2つめは

{ 
  sites { 
    name
    contentdata {
      label
    }
  }
}

contentdataの中にlabelだけを書いているので、結果もlabelだけが返ってきます。

Restではエンドポイントごとに決められた結果が返ってくるので、ユーザ側(番う側)は不必要なデータも受け取る必要がありました。
* *** 追記 MTの場合、コンテンツデータ一覧取得APIでfieldsを追加することで必要なフィールドのみに絞り込むことができますが、そういった便利実装をしていない限りは実装側が決めたフィールド全てが返ってきます。***
GraphQLの場合はユーザ側が必要なフィールドを書くことで自分が必要なデータのみを受け取ることができます。

まとめ

今回説明したのはGraphQLのすご〜く浅い部分だけで、
もっと色々と特徴や便利な部分はあるのですが、
私の勉強不足で人に説明できるほど理解が追いついていないため、ここまでとなります。

プラグインを作った時に使用したモジュールの話や詰まったポイントとかも書きたかったのですが、自分の中でもまとめきれていないので、またいつか書きたいと思います。

MTのAPIは使ったことがあるけど、GraphQLって何かは知らないという方に少しでもそういうものか、ふ〜んと思ってもらえれば、嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした