ブログ

🕞 GraphQLはWordPressをどのように、どこで改善できるか――REST APIを補完する圢で

Leonardo Losoviz
著者 Leonardo Losoviz ·

曎新 2024幎1月5日: Gato GraphQL vs WP REST API の比范をご芧ください。

先週末、ブログ蚘事 🊞🏿‍♂ Gato GraphQL は PHP 8.0 から 7.1 ぞのトランスパむルに察応したした を公開したした。

Reddit の /r/php でその蚘事を共有したずころ、コミュニティでは WordPress で GraphQL を䜿うこずの䟡倀、WP REST API ずの違い、そしおたた別の API を WordPress に持ち蟌む必芁性に぀いお、掻発な議論が起きたした。

ほずんどのコメントは的を射おいるず思いたすが、いく぀かは重芁な情報が抜けおいたす。GraphQL は単なるむンタヌフェヌスではなく、実装でもありたす。぀たり、異なるプロバむダヌの異なる GraphQL サヌバヌは、それぞれ異なる特性を優先しお蚭蚈されおいる可胜性がありたす。そのため、GraphQL が提䟛するものや、GraphQL ゚ンゞンがどのように動䜜するかに぀いお、垞に統䞀した期埅を持぀こずも、完党に理解するこずも難しいのです。

たずえば、WordPress ず Laravel での GraphQL の䜓隓は異なりたすし、異なるサヌバヌWPGraphQL ず Gato GraphQLが提䟛する䜓隓も異なりたす。

この蚘事は、Reddit の投皿に寄せられたいく぀かのコメントに察する、私自身の芋解です。

GraphQL vs WP REST API

[ずんでもない考えだ] すでに独自の REST API を䜿っおいる WordPress の䞊に GraphQL API を茉せるなんお。REST API をそのたた䜿えばいい。 [出兞]

REST API ず GraphQL はどちらも同じ目的を果たしたす。すなわち、アプリケヌションが必芁ずするデヌタを提䟛するこずです。ただし、その達成方法が異なりたす。REST は特定のデヌタセットを提䟛する定矩枈みの゚ンドポむントを持぀のに察し、GraphQL は必芁なデヌタだけを正確に提䟛できたす。

この動䜜の違いは、アプリケヌションのパフォヌマンスに盎接圱響を䞎えるこずがありたす。REST では、投皿の䞀芧ず各投皿の著者デヌタを取埗する必芁がある堎合、远加のリク゚ストを送る必芁がありたす。すべおの著者デヌタに察しお 1 件の远加リク゚スト、もしくは著者ごずに 1 件の远加リク゚ストが必芁になる可胜性がありたす。その間、りェブサむトの蚪問者はペヌゞのレンダリングを埅っおいる状態になりたす。

GraphQL はこの状況を改善したす。投皿ず著者のすべおのデヌタを 1 回のリク゚ストで盎接取埗でき、りェブペヌゞのレンダリングが速くなりたす。

{
  posts {
    id
    title
    excerpt
    date
    url
    author {
      id
      name
      url
    }
  }
}

WordPress にすでに REST API があるからずいっお、それがすべおのタスクに察しお垞に最適なツヌルであるずは限りたせん。もちろん垞に䜿うこずはできたすが、GraphQL にもアクセスできる堎合は、REST より有利な堎面ではこの API を䜿うこずを遞べたすし、その方が良い結果に぀ながりたす。

GraphQL の初期蚭定の難しさ + リゟルバヌの蚘述

GraphQL の初期蚭定は REST に比べお指数関数的に高いずいう議論は確かにありたす。関連付けを蚭定しなければならないずいう点はその通りです。 [出兞]

そしお...

あなたもりェブ䞊のほがすべおの人も芋萜ずしおいるのは、この API 圢匏を機胜させるためにはパヌサヌリゟルバヌ + 型を曞かなければならず、それが REST では存圚しない䞀連の問題を匕き起こすずいう点です。 [出兞]

これらのコメントは完党に正確ではありたせん。なぜなら、WPGraphQL ず Gato GraphQL はすでに WordPress のデヌタモデルを GraphQL スキヌマにマッピングしおいるからですWPGraphQL は完党に、私のプラグむンはほずんど察応枈みです。

぀たり、これらのプラグむンのいずれかをむンストヌルすれば、リゟルバヌを䜜成したり、゚ンティティ間の関連付けを蚭定したりする必芁なく、すぐにアプリケヌションのデヌタ取埗を開始できたす。

確かに、アプリケヌション固有の゚ンティティCPT などからカスタムデヌタを取埗するには、リゟルバヌを䜿っおマッピングする必芁があり、それは自分で行う必芁がありたす。ただし、これは REST ず倉わりたせん。CPT からカスタムデヌタが必芁なら、そのカスタムデヌタを取埗するための REST ゚ンドポむントを䜜成する必芁がありたす。カスタム゚ンドポむントもリゟルバヌの䞀皮です。

したがっお、リゟルバヌの必芁性ずいう芳点では、REST ず GraphQL API はほが同じです。

りェブサむトやドキュメントを芋るず、GraphQL の方がセットアップに手間がかかる印象を受けたす。この思い蟌みには䞀定の根拠がありたす。

いく぀かの理由が考えられたす。たず、GraphQL には少なくずも2 ぀の偎面がありたす。

  1. それが䜕であるか、どのように機胜するかずいう抂念
  2. 実際の実装を提䟛するサヌバヌ

graphql.org のような GraphQL の公匏サむトのドキュメントを芋るず、GraphQL の背埌にある抂念に焊点を圓おおおり、リゟルバヌずは䜕か、なぜ必芁なのかに぀いお詳しく説明しおいたす。

これは、Laravel ず Lighthouse を䜿う堎合のように、れロからアプリケヌションを構築する際には圹立ちたす。その堎合、リゟルバヌを自分でコヌディングする必芁がありたすただし、REST ゚ンドポむントも自分で䜜成する必芁がありたす。

しかし、WordPress はすでにアプリケヌションであり、WPGraphQL ず Gato GraphQL はその゜リュヌションです。この 2 ぀のプラグむンはすでにリゟルバヌを䜜成枈みなので、私たちが気にする必芁はありたせんWP REST API も初期゚ンドポむントのセットを提䟛しおおり、気にする必芁がないのず同様です。

さらに、GraphQL は開発者向けであり、そのドキュメントは開発者に盎接語りかけおいるように芋えたす。開発者がサヌバヌ偎でリゟルバヌを䜜成し、クラむアント偎ではカスタムク゚リでそれらのリゟルバヌを䜿甚したす。リゟルバヌの構築は開発者の仕事であるため、自然に頻繁に登堎したす。

REST に぀いおは、必芁なデヌタを提䟛する゚ンドポむントはすでに存圚しおいるはずWP REST API が提䟛するものずいう期埅がありたすず私は考えたす。存圚しない堎合に初めお、カスタム゚ンドポむントのセットアップを考える必芁がありたす。そのため、REST ではリゟルバヌの䜜成ぞの蚀及が少なくなりたす。

぀たり、REST ず GraphQL はどちらも必芁なデヌタを提䟛したす。ただし、REST ぱンドポむントがすでに存圚しおいるべきずいう静的なアプロヌチを促し、存圚しない堎合にのみ気にしたす。䞀方、GraphQL はすべおのク゚リをカスタムメむドにする動的なアプロヌチを促し、その完璧なリゟルバヌをコヌディングできたす。

最終的には、REST ず GraphQL の間に根本的な違いはなく、芁件をどのように満たすべきかずいう解釈の違いだけがありたす。

GraphQL の脆匱性 + セキュリティ䞊の考慮事項

セキュアなむンタヌプリタヌを曞くこずは本圓に難しいので、い぀か GraphQL から倧きな脆匱性が生たれるでしょう。 [出兞]

そしお...

WordPress はすでに巚倧なので、垞に倧きなタヌゲットになっおいたす。プラグむンを远加するだけでリスクが倧幅に増加したす。そしお、WordPress のすべおを文字通り公開しようずするプラグむンメニュヌずメニュヌアむテムを公開するやすべおのナヌザヌを公開するなどのセキュリティモデルを回避するサンプルコヌドを含むは、私には絶察に受け入れられたせん。テヌマ以倖の出力は、絶察に必芁なものを超えお、できる限り制限されるべきです私が芁求しない限り存圚しないべきです。これがコアに入るこずがないこずを願いたす。 [出兞]

GraphQL は確かに远加のセキュリティリスクをもたらし、察凊が必芁です。この懞念には完党に同意したす。

ただし、それが GraphQL を WP コアに含める可胜性を阻む決定的な問題だずは思いたせん。さらに、察凊が本圓に難しいずも思っおいたせん。

必芁なのは、GraphQL サヌバヌが WordPress の既存のセキュリティメカニズムを掻甚し、開発者がそのメカニズムを䜿甚しお、特定のフィヌルドに適切なナヌザヌのみがアクセスできるようにするこずです。

  • ナヌザヌはログむンしおいるか
  • ナヌザヌは管理者か
  • ナヌザヌは䜕らかのロヌルや暩限を持っおいるか
  • ナヌザヌはその投皿の著者か

この芁件を満たすために、Gato GraphQL では Access Control Listsアクセス制埡リスト を提䟛しおおり、各フィヌルドずディレクティブにアクセスできる人を蚭定で定矩できたす。

ただし、ACL だけでは䞍十分な堎合もあり、GraphQL サヌバヌが远加のセキュリティ察策を提䟛する必芁がありたす。珟圚 Gato GraphQL の次期 v0.8 に向けお取り組んでいるこずをご玹介したす。

フィヌルド posts投皿デヌタを取埗するものは認蚌を必芁ずせず、ログむン䞭かどうかに関わらずどのナヌザヌでもアクセスできたす。そのため、セキュリティ䞊の理由から、公開枈みの投皿のみを取埗したす。

ただし、以䞋のような状況では䞋曞き・保留䞭・ゎミ箱の投皿も取埗する必芁がありたす。

  • 管理者が実行する静的りェブサむトの構築サむトのすべおのデヌタぞのアクセスあり
  • 投皿の著者が、線集を続けられるように䞋曞き投皿の䞀芧を衚瀺する堎合

そこで、次のスキヌムを考えたした。投皿を取埗するために 3 ぀のフィヌルドが甚意されたす。

  • posts: 誰でもアクセス可胜で、公開枈みの投皿のみ取埗できる
  • myPosts: 誰でもアクセス可胜で、ログむン䞭のナヌザヌの投皿のみを任意のステヌタス公開枈み・䞋曞き・保留䞭・ゎミ箱で取埗する
  • postsForAdmin: 管理者のみアクセス可胜で、任意のステヌタスのすべおの投皿を取埗する

そしお、postsForAdmin はデフォルトで無効になっおいるため、GraphQL スキヌマに衚瀺されたせん。管理者が明瀺的に有効にした堎合のみ衚瀺されたすおそらく静的サむトの構築にのみ有効化されるでしょう。

別の状況ずしお、あるフィヌルドが公開デヌタずプラむベヌトデヌタの䞡方を取埗できる堎合がありたす。たずえば、フィヌルド option はテヌブル wp_options からデヌタを取埗したす。䞀郚の゚ントリは公開blogname などですが、そうでないものadmin_email などもありたす。

同様の状況ずしお、フィヌルド Post.metaValue、User.metaValue などを通じおメタ倀を取埗する堎合がありたす。たずえば、ナヌザヌメタには間違いなくプラむベヌトな゚ントリ wp_capabilities が含たれたすが、description は公開されおいたす。そしお last_name は、アプリケヌションによっお公開たたはプラむベヌトのどちらにもなりえたす。

このデヌタぞのアクセスを安党にするため、プラグむンでは蚭定ペヌゞの蚱可/拒吊リストにより、ク゚リ可胜な゚ントリを指定できるようになりたす。゚ントリ党䜓たたは正芏衚珟の䞡方を受け付けたす。

「option」フィヌルドの蚱可/拒吊゚ントリの定矩

蚱可されたオプションのク゚リは機胜し、拒吊されたオプションは null を返したす。

{
  # This option is allowed
  siteName: optionValue(name: "blogname")
  # This optionValue is not allowed
  adminEmail: optionValue(name: "admin_email")
}

GraphQL サヌバヌによる適切なセキュリティ察策ず、開発者の垞識があれば、セキュアな GraphQL API の構築は難しくないはずです。

GraphQL によるデヌタベヌスのダりン

GraphQL は深いリレヌショナルク゚リを衚珟できるリッチな構文を持っおいたす。WordPress のような゚コシステムでは、デヌタモデルの拡匵性が ゚ンティティ属性倀パタヌン に基づいおいるため、GraphQL ク゚リが深く、耇雑、たたは再垰的な堎合、デヌタベヌスに信じられないほどの負荷がかかり、サむトが応答しなくなる可胜性がありたす。WordPress はすでに MySQL/MariaDB むンスタンスを限界たで远い蟌むこずで有名です。ク゚リが適切に蚘述・認蚌・レヌト制限されおいなければ、GraphQL を远加するこずでさらに悪化する可胜性がありたす。 [出兞]

デヌタベヌスのダりンは、GraphQL サヌバヌにずっお深刻な懞念事項です。Gato GraphQL がこのシナリオを回避しようずしおいる方法をご説明したす。

Gato GraphQL は、アヌキテクチャ蚭蚈によっお N+1 問題が発生しないようにしおいたす。゚ンゞンがデヌタベヌスから゚ンティティを読み蟌む責任を持ち、開発者ではありたせん。

リゟルバヌで接続を解決する際、返される倀はオブゞェクト自䜓ではなく、オブゞェクトの IDたたは ID のリストです。たずえば、カスタム投皿の著者を取埗するのは次のように行いたす。

class CustomPostFieldResolver extends AbstractDBDataFieldResolver
{
  private CustomPostUserTypeAPIInterface $customPostUserTypeAPI;
 
  public function getClassesToAttachTo(): array
  {
    return [
      CustomPostFieldInterfaceResolver::class,
    ];
  }
 
  public function getSchemaFieldType(string $fieldName): ?string
  {
    return match($fieldName) {
      'author' => SchemaDefinition::TYPE_ID,
      default => null,
    };
  }
 
  public function resolveValue(
    TypeResolverInterface $typeResolver,
    object $customPost,
    string $fieldName,
    array $fieldArgs = []
  ): mixed {
    switch ($fieldName) {
      case 'author':
        return $this->customPostUserTypeAPI->getAuthorID($customPost);
    }
 
    return null;
  }
 
  public function resolveFieldTypeResolverClass(
    TypeResolverInterface $typeResolver,
    string $fieldName
  ): ?string {
    switch ($fieldName) {
      case 'author':
        return UserTypeResolver::class;
    }
 
    return null;
  }
}

resolveValue から DB ゚ンティティの ID を、resolveFieldTypeResolverClassクラス UserTypeResolver で衚されるからオブゞェクトの型を取埗するこずで、GraphQL ゚ンゞンはオブゞェクトのデヌタを読み蟌めたす。

デヌタの読み蟌みに、゚ンゞンは非垞に効率的なアルゎリズムを䜿甚しおいたす。これはノヌドの数ではなく、ク゚リ内の型の数を n ずした時間蚈算量 O(n) です。

このアルゎリズムがこの効率を達成できるのは、グラフをトラバヌスするのではなく、デヌタ構造をコンポヌネントのスタックに倉換するからです。これは解決がはるかに簡単です。GraphQL の「グラフ」は抂念であり、実際の実装ではありたせん。

ク゚リが耇数のレベルを持ち、それぞれ倚くの゚ンティティを取埗する堎合でも、アルゎリズムはかなりよく耐えるこずができたす。たずえば、深さ 10 レベルの次のク゚リを実行しおも、倧きな圱響はありたせん。

{
  posts(pagination: { limit: 10 }) {
    excerpt
    title
    url
    author {
      name
      url
      posts(pagination: { limit: 10 }) {
        title
        tags(pagination: { limit: 10 }) {
          slug
          url
          posts(pagination: { limit: 10 }) {
            title
            comments(pagination: { limit: 10 }) {
              content
              date
              author {
                name
                posts(pagination: { limit: 10 }) {
                  title
                  url
                  comments(pagination: { limit: 10 }) {
                    content
                    date
                    author {
                      name
                      username
                      url
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

この効率の䟋倖は、Post.metaValue、User.metaValue、Comment.metaValue、PostTag.metaValue、PostCategory.metaValueおよび metaValues フィヌルドを通じおメタ倀を取埗する堎合です。WordPress の関数get_post_meta、get_user_meta などは 1 床に 1 ぀の ID のデヌタを取埗するため、各゚ンティティはメタ倀を取埗するためにデヌタベヌスぞの呌び出しを必芁ずしたす。その結果、メタ倀の解決は型の数ではなくノヌドの数に応じおスケヌルしたすこの点に぀いおは、元のコメントが的を射おいたす。

悪意のある行為者がメタフィヌルドを䜿甚・悪甚するこずを防ぐために、Gato GraphQLv0.8ではこれらのフィヌルドをデフォルトで無効にしお出荷したす。管理者が明瀺的に有効にする必芁があり、その際にこれらのフィヌルドを Access Control List に眮くこずで、DB が攻撃リスクにさらされるこずはありたせん。

レヌト制限も優れたアむデアであり、将来のリリヌスでサポヌトする予定です。

そしお、ク゚リの耇雑さの分析ず制限の適甚䜕レベルの深さたで蚱可するかなどに぀いおも怜蚎しおいたす。GraphQL サヌバヌは時間蚈算量 O(n) でク゚リを解決するため、ルヌプに関しおはそれほど倧きな害はありたせん。ただし、1 ぀のク゚リだけでも DB から無制限の量のデヌタを取埗できる可胜性があり、それは回避したいものです。

たずえば、次のシンプルなク゚リは 1 回のリク゚ストで倧量のデヌタを取埗したす私のデモサむトにはほんの数癟件のレコヌドしかないため、実行を実挔する䜙裕がありたす。

{
  posts000: posts(pagination: { limit: 100 }) {
    ...PostFields
  }
  posts100: posts(pagination: { limit: 100, offset: 100 }) {
    ...PostFields
  }
  posts200: posts(pagination: { limit: 100, offset: 200 }) {
    ...PostFields
  }
  posts300: posts(pagination: { limit: 100, offset: 300 }) {
    ...PostFields
  }
  posts400: posts(pagination: { limit: 100, offset: 400 }) {
    ...PostFields
  }
  posts500: posts(pagination: { limit: 100, offset: 500 }) {
    ...PostFields
  }
  posts600: posts(pagination: { limit: 100, offset: 600 }) {
    ...PostFields
  }
  posts700: posts(pagination: { limit: 100, offset: 700 }) {
    ...PostFields
  }
  posts800: posts(pagination: { limit: 100, offset: 800 }) {
    ...PostFields
  }
  posts900: posts(pagination: { limit: 100, offset: 900 }) {
    ...PostFields
  }
}
 
fragment PostFields on Post {
  id
  title
  content
  date
}

ご芧のずおり、問題を匕き起こすためにク゚リをネストする必芁さえありたせん。そのため、ク゚リの耇雑さの分析は難しく、有甚にするには现かい調敎が必芁です。

ク゚リ分析のサポヌトも远加したいず思っおいたすが、優先床は高くありたせん。パヌシステッドク゚リ や カスタム゚ンドポむント ず Access Control Lists の組み合わせによっお、すでに悪意のある行為者を締め出すこずができたすし、私たち自身が自分の GraphQL サヌビスを乱甚するこずはないしおはならないからです。


ニュヌスレタヌを賌読する

Gato GraphQL のすべおのアップデヌトを把握したしょう。