コンテンツフラグメントと共に使用する AEM GraphQL API graphql-api-for-use-with-content-fragments

Adobe Experience Manager(AEM) のコンテンツフラグメントを AEM GraphQL API と共に使用してヘッドレスコンテンツ配信を実現する方法を説明します。

コンテンツフラグメントと共に使用する AEM GraphQL API は、オープンソースの標準 GraphQL API に大いに基づいています。

AEM の GraphQL API を使用すると、ヘッドレス CMS 実装の JavaScript クライアントにコンテンツフラグメントを効率的に配信できます。

  • REST で API リクエストの反復を回避
  • 特定の要件に限定された配信を確保
  • 1 つの API クエリへの応答としてレンダリングに必要なものだけを一括配信
NOTE
GraphQL は現在 Adobe Experience Manager(AEM)の 2 つの(別々の)シナリオで使用されています。

前提条件 prerequisites

GraphQL を使用しているお客様は、GraphQL インデックスパッケージ 1.0.5 を使用した AEM コンテンツフラグメントをインストールする必要があります。詳しくは、リリースノートを参照してください。

GraphQL API graphql-api

GraphQL とは次のことを意味します。

  • …API のクエリ言語と、既存のデータを使用してこれらのクエリを満たすランタイムです。GraphQL は、API でデータの完全で理解可能な説明を提供します。必要なものと不要なものを正確に求める力をクライアントに提供し、API を時間の経過と共に発展させ、強力な開発者ツールを実現します」。

    GraphQL.org を参照

  • …柔軟な API レイヤー用のオープンな仕様。GraphQL を既存のバックエンドに重ね合わせ、これまでにない速さで製品を構築できます…

    Explore GraphQL を参照してください。

  • 「…2012 年に Facebook 社内で開発されたデータクエリ言語および仕様です。その後、2015 年には公式にオープンソースとなりました。開発者の生産性を高め、転送データの量を最小限に抑えるために、REST ベースのアーキテクチャに代わる手段を提供します。GraphQL は、あらゆる規模の数百の組織により実稼働環境で使用されています…」

    GraphQL Foundation を参照してください。

GraphQL API について詳しくは、(多くのリソースの中でも特に)次の節を参照してください。

AEM 用 GraphQL の実装は、標準の GraphQL Java™ ライブラリをベースにしています。以下を参照してください。

GraphQL 用語 graphql-terminology

GraphQL では次を使用します。

ベストプラクティスを含む包括的な詳細については、「(GraphQL.org) GraphQL の概要」を参照してください。

GraphQL クエリタイプ graphql-query-types

GraphQL では、次のいずれかを返すクエリを実行できます。

AEM は、クエリ(両方のタイプ)を Dispatcher と CDN によってキャッシュできる永続クエリに変換する機能を提供します。

GraphQL クエリのベストプラクティス(Dispatcher と CDN) graphql-query-best-practices

永続クエリは、パブリッシュインスタンスで次のように使用することをお勧めします。

  • キャッシュされます
  • AEM で一元管理されます
NOTE
通常、オーサーには Dispatcher/CDN がないので、永続クエリをテストする以外に使用してもパフォーマンス上のメリットはありません。

POST リクエストを使用する GraphQL クエリは、キャッシュされないのでお勧めしません。そのため、デフォルトのインスタンスでは、Dispatcher はそれらのクエリをブロックするように設定されています。

GraphQL は GET リクエストもサポートしていますが、これらのリクエストは制限(URL の長さなど)に達する可能性があり、これは永続クエリを使用することで回避できます。

詳しくは、永続クエリのキャッシュの有効化を参照してください。

NOTE
直接クエリを実行する機能は、将来、廃止される可能性があります。

GraphiQL インターフェイス graphiql-interface

標準の GraphiQL インターフェイスの実装は、AEM GraphQL で使用できます。

NOTE
GraphiQL は AEM のすべての環境に含まれています(ただし、エンドポイントを設定した場合にのみアクセス可能/表示可能になります)。
以前のリリースでは、GraphiQL IDE をインストールするためにパッケージが必要でした。このパッケージをインストール済みの場合、削除できます。

このインターフェイスを使用すると、クエリを直接入力してテストできます。

例:

  • http://localhost:4502/content/graphiql.html

構文のハイライト表示、オートコンプリート、自動候補表示などの機能と共に、履歴およびオンラインドキュメントが用意されています。

GraphiQL インターフェイス

NOTE
GraphiQL IDE の使用を参照してください。

オーサー環境とパブリッシュ環境の使用例 use-cases-author-publish-environments

使用例は、AEM 環境のタイプによって異なる場合があります。

  • パブリッシュ環境の使用目的:

    • JS アプリケーションのデータのクエリ(標準の使用例)
  • オーサー環境の使用目的:

    • 「コンテンツ管理用」のデータのクエリ:

      • AEM の GraphQL は読み取り専用の API です。
      • REST API は、CR(U)D の操作に使用できます。

権限 permission

Assets にアクセスするには権限が必要です。

GraphQL クエリは、基になるリクエストの AEM ユーザーの権限で実行されます。ユーザーが一部の(Assets として保存された)フラグメントへの読み取りアクセス権を持っていない場合、ユーザーは結果セットの一部になりません。

また、ユーザーが GraphQL クエリを実行するには、GraphQL エンドポイントへのアクセス権を持っている必要があります。

スキーマ生成 schema-generation

GraphQL は、型指定された API です。つまり、データは型別に明確に構造化され編成される必要があります。

GraphQL の仕様には、特定のインスタンス上のデータをクエリするための堅牢な API を作成する方法に関する一連のガイドラインが用意されています。これらのガイドラインを完了するには、クライアントは、クエリに必要なすべての型を含むスキーマを取得する必要があります。

コンテンツフラグメントの場合、GraphQL スキーマ(構造とタイプ)は、有効 ​なコンテンツフラグメントモデルとそれらのデータタイプに基づいています。

CAUTION
有効 ​になっているコンテンツフラグメントモデルから派生した)すべての GraphQL スキーマは、GraphQL エンドポイントを通じて読み取り可能です。
この機能は、この方法で機密データが漏洩する可能性があるので、機密データを利用できないようにする必要があることを意味します。例えば、モデル定義のフィールド名として存在する情報が含まれます。

例えば、ユーザーが Article というコンテンツフラグメントモデルを作成した場合、AEM は GraphQL タイプ ArticleModel を生成します。このタイプに含まれるフィールドは、モデルで定義されているフィールドとデータタイプに対応しています。さらに、articleByPatharticleList など、このタイプで動作するクエリのいくつかのエントリポイントを作成します。

  1. コンテンツフラグメントモデル:

    GraphQL で使用するコンテンツフラグメントモデル

  2. 対応する GraphQL スキーマ(GraphiQL 自動生成ドキュメントからの出力):
    コンテンツフラグメントモデルに基づく GraphQL スキーマ

    この画像では、生成された型 ArticleModel に複数のフィールドが含まれていることがわかります。

    • そのうちの 3 つ(authormainreferencearticle)は、ユーザーが管理しています。

    • その他のフィールドは AEM によって自動的に追加されたもので、特定のコンテンツフラグメントに関する情報を提供する便利な手段となっています。この例では、( ヘルパーフィールド) _path, _metadata, _variations.

  3. ユーザーが Article モデルに基づいてコンテンツフラグメントを作成すると、GraphQL を使用してそれをクエリできます。例については、(GraphQL で使用するコンテンツフラグメント構造のサンプルに基づいた)サンプルクエリを参照してください。

AEM 用 GraphQL では、スキーマには柔軟性があります。この柔軟性は、コンテンツフラグメントモデルを作成、更新、削除するたびに、スキーマが自動生成されることを意味します。また、コンテンツフラグメントモデルを更新すると、データスキーマキャッシュも更新されます。

Sites GraphQL サービスは、コンテンツフラグメントモデルに対する変更を(バックグラウンドで)リッスンします。更新が検出されると、スキーマのその部分だけが再生成されます。この最適化により、時間が節約され、安定性も確保されます。

例えば、次のようになります。

  1. Content-Fragment-Model-1Content-Fragment-Model-2 を含んだパッケージをインストールすると、

    1. Model-1 および Model-2 の GraphQL タイプが生成されます。
  2. 次に Content-Fragment-Model-2 を変更すると、

    1. Model-2 GraphQL のタイプのみが更新されます。

    2. 一方で Model-1 は変わりません。

NOTE
この詳細は、REST API を使用してコンテンツフラグメントモデルの一括更新を行う場合などに留意することが大切です。

スキーマは、GraphQL クエリと同じエンドポイントを通じて提供され、クライアントはスキーマが拡張子 GQLschema で呼び出されることに対処します。例えば、/content/cq:graphql/global/endpoint.GQLschema で単純な GET リクエストを実行すると、text/x-graphql-schema;charset=iso-8859-1 の Content-type を持つスキーマが出力されます。

スキーマの生成 - 未公開のモデル schema-generation-unpublished-models

コンテンツフラグメントがネストされると、親のコンテンツフラグメントモデルは公開されますが、参照モデルは公開されません。

NOTE
AEM ユーザーインターフェイスはこのような問題を回避しますが、プログラムを使用して、またはコンテンツパッケージを使用して公開すると、この問題が発生する可能性があります。

この場合、AEM は親コンテンツフラグメントモデルの​ 不完全な ​スキーマを生成します。非公開のモデルに依存するフラグメント参照がスキーマから削除されることを意味します。

フィールド fields

スキーマ内には、次の 2 つの基本的なカテゴリに属する個々のフィールドがあります。

  • ユーザーが生成するフィールド

    選択されたフィールドタイプを使用して、コンテンツフラグメントモデルの設定方法に基づいてフィールドが作成されます。フィールド名は、データタイプ ​の「プロパティ名」フィールドから取得されます。

    • ユーザーが特定のデータタイプを設定できるので、レンダリング形式 ​設定も考慮する必要があります。例えば、1 行のテキストフィールドに複数の 1 行のテキストを含めるように設定するには、ドロップダウンから「multifield」を選択します。
  • AEM の GraphQL も、複数のヘルパーフィールドを生成します。

    これらは、コンテンツフラグメントを識別するためや、コンテンツフラグメントに関する詳細を取得するために使用されます。

データタイプ data-types

AEM 用 GraphQL では一連のタイプをサポートしています。サポートされているすべてのコンテンツフラグメントモデルデータ型と、それに対応する GraphQL 型を以下の表に示します。

コンテンツフラグメントモデル - データ型
GraphQL の型
説明
1 行のテキスト
String[String]
作成者名、場所名などの単純な文字列に使用します。
複数行のテキスト
String
記事の本文などのテキストを出力するために使用します
Number
Float[Float]
浮動小数点数と整数を表示するために使用します
ブーリアン
Boolean
チェックボックスを表示するために使用します(単純な真/偽のステートメント)
日時
Calendar
日時を ISO 8086 形式で表示するために使用します。選択したタイプに応じて、AEM GraphQL で使用できるフレーバーは、onlyDateonlyTimedateTime の 3 つです。
定義済みリスト
String
モデルの作成時に定義されたオプションのリストに含まれるオプションを表示するために使用します
タグ
[String]
AEM で使用されているタグを表す文字列のリストを表示するために使用します
コンテンツ参照
String
AEM 内の別のアセットへのパスを表示するために使用します
フラグメント参照
モデル型の

単一のフィールド:Model - 直接参照されるモデル型

マルチフィールド(1 つの参照タイプ):[Model] - 型の配列 Model(配列から直接参照)

複数の参照型を持つマルチフィールド:[AllFragmentModels] - 和集合型を持つ配列から参照される、すべてのモデル型の配列
モデルの作成時に定義された、特定のモデル型の 1 つ以上のコンテンツフラグメントの参照に使用します

ヘルパーフィールド helper-fields

ユーザー生成フィールドのデータ型に加えて、AEM 用 GraphQL では、コンテンツフラグメントの識別やコンテンツフラグメントに関する追加情報の提供に役立つ多数の​ ヘルパー ​フィールドも生成されます。

これらのヘルパーフィールドは、ユーザーが定義したものと自動生成されたものを区別するために、先頭に _ が付きます。

パス path

パスフィールドは、AEM GraphQL で識別子として使用され、AEM リポジトリ内のコンテンツフラグメントアセットのパスを表します。このパスは、次の理由で、コンテンツフラグメントの識別子として選択されます。

  • AEM 内で一意である
  • 取得しやすい

次のコードでは、コンテンツフラグメントモデル Person に基づいて作成されたすべてのコンテンツフラグメントのパスを表示します。

{
  personList {
    items {
      _path
    }
  }
}

特定のタイプのコンテンツフラグメントを 1 つ取得するには、まずそのパスも決定する必要があります。例:

{
  authorByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _path
      firstName
      name
    }
  }
}

サンプルクエリ - ある 1 つの特定の都市フラグメントを参照してください。

メタデータ metadata

また、AEM では GraphQL を通じて、コンテンツフラグメントのメタデータも公開します。メタデータは、次のようなコンテンツフラグメントを記述する情報です。

  • コンテンツフラグメントのタイトル
  • サムネールのパス
  • コンテンツフラグメントの説明
  • および作成された日付など。

メタデータはスキーマエディターで生成され、特定の構造を持たないので、コンテンツフラグメントのメタデータを公開するために GraphQL 型 TypedMetaData が実装されました。TypedMetaData では、次のスカラー型でグループ化された情報を公開します。

フィールド
stringMetadata:[StringMetadata]!
stringArrayMetadata:[StringArrayMetadata]!
intMetadata:[IntMetadata]!
intArrayMetadata:[IntArrayMetadata]!
floatMetadata:[FloatMetadata]!
floatArrayMetadata:[FloatArrayMetadata]!
booleanMetadata:[BooleanMetadata]!
booleanArrayMetadata:[booleanArrayMetadata]!
calendarMetadata:[CalendarMetadata]!
calendarArrayMetadata:[CalendarArrayMetadata]!

各スカラー型は、名前と値の 1 つのペアを表すか、名前と値のペアの配列を表します。このペアの値は、グループ化されたときの型になります。

例えば、コンテンツフラグメントのタイトルを取得する場合は、このプロパティが String 型プロパティであるためで、すべての String 型メタデータをクエリすることになります。

メタデータをクエリするには、次のようにします。

{
  personByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _path
      _metadata {
        stringMetadata {
          name
          value
        }
      }
    }
  }
}

生成された GraphQL スキーマを表示するには、すべてのメタデータ GraphQL 型を表示します。すべてのモデルタイプは同じ TypedMetaData を持ちます。

NOTE
標準メタデータと配列メタデータの違い
StringMetadataStringArrayMetadata はどちらも、リポジトリに格納されているものについての指定であり、その取得手段についての指定ではありません。
例えば、stringMetadata フィールドを呼び出すことで、String としてリポジトリに保存されているすべてのメタデータの配列を受け取ります。また、stringArrayMetadata を呼び出した場合、String[] としてリポジトリに格納されているすべてのメタデータの配列を受け取ります。

詳しくは、メタデータのサンプルクエリ - 「GB」という賞のメタデータのリストを参照してください。

バリエーション variations

コンテンツフラグメントのバリエーションに対するクエリを簡略化するために、_variations フィールドが実装されています。次に例を示します。

{
  personByPath(_path: "/content/dam/path/to/fragment/john-doe") {
    item {
      _variations
    }
  }
}
NOTE
技術的には元のデータ(UI で​ マスター ​として参照される)は明示的なバリエーションと見なされず、「_variations」フィールドには master バリエーションが含まれません。

詳しくは、サンプルクエリ - 名前付きバリエーションを持つすべての都市を参照してください。

NOTE
コンテンツフラグメントに指定されたバリエーションが存在しない場合は、元のデータ(プライマリバリエーション)が(フォールバック)デフォルトとして返されます。

GraphQL 変数 graphql-variables

GraphQL では、クエリに変数を含めることができます。詳細情報は、変数に関する GraphQL のドキュメントを参照してください。

例えば、特定のバリエーションを持つ Article タイプのコンテンツフラグメントをすべて取得するには、次のように、GraphiQL で変数 variation を指定します。

GraphQL 変数

### query
query GetArticlesByVariation($variation: String!) {
    articleList(variation: $variation) {
        items {
            _path
            author
            _variations
        }
    }
}

### in query variables
{
    "variation": "uk"
}

GraphQL ディレクティブ graphql-directives

GraphQL では、GraphQL ディレクティブと呼ばれる変数に基づいてクエリを変更する可能性があります。

例えば、変数 includePrice に基づいて、すべての AdventureModels のクエリに adventurePrice フィールドを含めることができます。

GraphQL ディレクティブ

### query
query GetAdventureByType($includePrice: Boolean!) {
  adventureList {
    items {
      adventureTitle
      adventurePrice @include(if: $includePrice)
    }
  }
}

### in query variables
{
    "includePrice": true
}

フィルタリング filtering

GraphQL クエリでフィルタリングを使用して、特定のデータを返すこともできます。

フィルタリングでは、論理演算子と論理式に基づいた構文を使用します。

最もアトミックな部分は、特定のフィールドの内容に適用できる単一の式です。フィールドの内容を指定された定数値と比較します。

例えば、次の式はフィールドの内容と値 some text を比較し、内容と値が等しい場合は成功します。それ以外の場合、式は失敗します。

{
  value: "some text"
  _op: EQUALS
}

次の演算子を使用して、フィールドを特定の値と比較できます。

演算子
タイプ
次の場合、式は成功します
EQUALS
StringIDBoolean
… 値がフィールドの内容と同じである
EQUALS_NOT
StringID
… 値がフィールドの内容と同じ​ ではない
CONTAINS
String
… フィールドの内容に値が含まれる({ value: "mas", _op: CONTAINS }ChristmasXmasmaster などと一致)
CONTAINS_NOT
String
… フィールドの内容に値が含まれて​ いない
STARTS_WITH
ID
… ID が特定の値で始まる({ value: "/content/dam/", _op: STARTS_WITH/content/dam/path/to/fragment と一致するが、/namespace/content/dam/something とは一致しない)
EQUAL
IntFloat
… 値がフィールドの内容と同じである
UNEQUAL
IntFloat
… 値がフィールドの内容と同じで​ はない
GREATER
IntFloat
… フィールドの内容が値より大きい
GREATER_EQUAL
IntFloat
… フィールドの内容が値以上である
LOWER
IntFloat
… フィールドの内容が値よりも小さい
LOWER_EQUAL
IntFloat
… フィールドの内容が値以下である
AT
CalendarDateTime
… フィールドの内容が値とまったく同じである(タイムゾーン設定を含む)
NOT_AT
CalendarDateTime
… フィールドの内容が値と同じで​ はない
BEFORE
CalendarDateTime
… 値によって示される時点が、フィールドの内容によって示される時点よりも前である
AT_OR_BEFORE
CalendarDateTime
… 値によって示される時点が、フィールドの内容によって示される時点より前または同じ時点である
AFTER
CalendarDateTime
… 値によって示される時点が、フィールドの内容によって示される時点よりも後である
AT_OR_AFTER
CalendarDateTime
… 値によって示される時点が、フィールドの内容によって示される時点より後または同じ時点である

また、次のように式の評価方法を変更する追加のオプションを指定できるタイプもあります。

オプション
タイプ
説明
_ignoreCase
String
文字列の大文字と小文字を無視します(例:time の値は TIMEtimetImE …に一致)
_sensitiveness
Float
float 値が同じと見なされるための一定のマージンを許可します(float 値の内部表現による技術的な制限を回避するため)。このオプションはパフォーマンスに悪影響を及ぼす可能性があるので、避ける必要があります。

式は、論理演算子(_logOp)を使用して設定に組み合わせることができます。

  • OR - 少なくとも 1 つの式が成功した場合、式のセットは成功します
  • AND - すべての式が成功した場合、式のセットは成功します(デフォルト)

各フィールドは、独自の式セットでフィルタリングできます。フィルター引数で指定されたすべてのフィールドの式セットは、最終的に独自の論理演算子で結合されます。

フィルター定義(filter 引数としてクエリに渡される)には次が含まれます。

  • 各フィールドのサブ定義(フィールド名を使用してアクセスできます。例えば、データ(フィールド)タイプの lastName フィールドのフィルターには lastName フィールドがあります)
  • 各サブ定義には、式セットを提供する _expressions 配列と、式を組み合わせる必要がある論理演算子を定義する _logOp フィールドが含まれます
  • 各式は、値(value フィールド)と演算子(_operator フィールド)によって定義され、フィールドの内容を比較する必要があります

項目を AND と組み合わせる場合は _logOp を、等しいかどうかを確認する場合は _operator を省略できます(これらの値はデフォルトのため)。

次の例は、大文字と小文字を区別せずに、ProvolastName である、または sjö を含むすべてのユーザーをフィルタリングする完全なクエリを示しています。

{
  authorList(filter: {
    lastname: {
      _logOp: OR
      _expressions: [
        {
          value: "sjö",
          _operator: CONTAINS,
          _ignoreCase: true
        },
        {
          value: "Provo"
        }
      ]
    }
  }) {
    items {
      lastName
      firstName
    }
  }
}

オプションの変数を使用してGraphQLクエリを実行する場合、特定の値が not オプションの変数に指定した場合、変数はフィルター評価で無視されます。 つまり、クエリ結果にはすべての値 ( null およびでない null(フィルター変数に関連するプロパティ)。

NOTE
次の場合、 null 値は 明示的に このような変数に指定した場合、フィルターは null 対応するプロパティの値。

例えば、以下のクエリでは、プロパティの値が指定されていません。 lastName:

query getAuthorsFilteredByLastName($authorLastName: String) {
  authorList(filter:
    {
      lastName: {_expressions: {value: $authorLastName}
      }}) {
    items {
      lastName
    }
  }
}

すべての作成者が返されます。

{
  "data": {
    "authorList": {
      "items": [
        {
          "lastName": "Hammer"
        },
        {
          "lastName": "Provo"
        },
        {
          "lastName": "Wester"
        },
        {
          "lastName": null
        },
         ...
      ]
    }
  }
}

ネストされたフィールドに対してフィルターを適用することもできますが、パフォーマンスの問題が発生する可能性があるため、お勧めしません。

その他の例については、以下を参照してください。

並べ替え sorting

NOTE
最高のパフォーマンスを得るには、GraphQL フィルタリングのページングと並べ替え用にコンテンツフラグメントを更新することを検討してください。

この機能を使用すると、指定したフィールドに従ってクエリ結果を並べ替えることができます。

並び替え条件:

  • フィールドパスを表すコンマ区切りの値のリスト

    • リスト内の最初のフィールドで、主な並べ替え順を定義します

      • 2 番目のフィールドは、主な並べ替え条件の 2 つの値が等しい場合に使用されます
      • 最初の 2 つの条件が等しい場合は 3 番目のフィールドを、などのように使用されます
    • field1.subfield.subfield などのドット表記になります。

  • (オプション)並べ替えの方向

    • ASC(昇順)または DESC(降順)。デフォルトでは ASC が適用されます
    • 方向は、フィールドごとに指定できます。つまり、あるフィールドを昇順で、別のフィールドを降順で並べ替えることができます(name、firstName DESC)

例:

query {
  authorList(sort: "lastName, firstName") {
    items {
      firstName
      lastName
    }
  }
}

次のようにすることもできます。

{
  authorList(sort: "lastName DESC, firstName DESC") {
    items {
        lastName
        firstName
    }
  }
}

また、nestedFragmentname.fieldname の形式を使用して、ネストされたフラグメント内のフィールドで並べ替えることもできます。

NOTE
この形式は、パフォーマンスに悪影響を及ぼす可能性があります。

例:

query {
  articleList(sort: "authorFragment.lastName")  {
    items {
      title
      authorFragment {
        firstName
        lastName
        birthDay
      }
      slug
    }
  }
}

ページング paging

NOTE
最高のパフォーマンスを得るには、GraphQL フィルタリングのページングと並べ替え用にコンテンツフラグメントを更新することを検討してください。

この機能を使用すると、リストを返すクエリタイプに対してページングを実行できます。 次の 2 つの方法があります。

  • List クエリの offsetlimit
  • Paginated クエリの firstafter

リストクエリ - オフセットと制限 list-offset-limit

...List クエリでは、offsetlimit を使用して、特定の結果サブセットを返すことができます。

  • offset:返される最初のデータセットを指定します
  • limit:返されるデータセットの最大数を指定します

例えば、完全な ​結果リストの 5 番目の記事から開始して、最大 5 つの記事を含む結果のページを出力するには、次のようにします。

query {
   articleList(offset: 5, limit: 5) {
    items {
      authorFragment {
        lastName
        firstName
      }
    }
  }
}
NOTE
  • ページングには安定した並べ替え順序が必要です。同じ結果セットの異なるページをリクエストする複数のクエリで正しく動作するようにします。デフォルトでは、結果セットの各項目のリポジトリパスを使用して、順序が常に同じであることを確認します。異なる並べ替え順を使用していて、その並べ替えを JCR クエリレベルで実行できない場合、パフォーマンスに悪影響を及ぼします。これは、ページが決定される前に結果セット全体をメモリに読み込む必要があるからです。

  • オフセットが大きいほど、JCR クエリの結果セット全体から項目をスキップするのに時間がかかります。大規模な結果セットに対する代替ソリューションは、first および after メソッドでページ分割されたクエリを使用することです。

ページ分割されたクエリ - first と after paginated-first-after

...Paginated クエリタイプは、ほとんどの ...List クエリタイプの機能(フィルタリング、並べ替え)を再利用しますが、offsetlimit 引数の代わりに、GraphQL カーソル接続仕様で定義されている firstafter 引数を使用します。GraphQL の概要では、堅苦しくない概要を見つけることができます。

  • first:返される最初の n 個の項目。
    デフォルトは、50 です。
    最大値は 100 です。
  • after:リクエストされたページの先頭を決定するカーソル。カーソルで表される項目は結果セットに含まれません。項目のカーソルは、edges 構造体の cursor フィールドによって決定されます。

例えば、完全な ​結果リスト内の指定されたカーソル項目から開始して、最大 5 つのアドベンチャーを含む結果のページを出力します。

query {
    adventurePaginated(first: 5, after: "ODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1") {
        edges {
          cursor
          node {
            title
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
    }
}
NOTE
  • デフォルトのページングでは、結果の順序が常に同じになるように、フラグメントを表すリポジトリノードの UUID を順序付けに使用します。sort を使用すると、UUID は一意の並べ替えを保証するために暗黙的に使用されます。同じ並べ替えキーを持つ 2 つの項目の場合も同様です。

  • 内部の技術的制約により、ネストされたフィールドに並べ替えとフィルタリングを適用すると、パフォーマンスが低下します。そのため、ルートレベルで格納されたフィルターや並べ替えのフィールドを使用してください。これは、ページ分割された大きな結果セットをクエリする場合にも推奨される方法です。

GraphQL の永続クエリ - Dispatcher でのキャッシュの有効化 graphql-persisted-queries-enabling-caching-dispatcher

CAUTION
Dispatcher でのキャッシュが有効な場合、CORS フィルターが不要なので、セクションを無視できます。

永続化されたクエリのキャッシュは、Dispatcher ではデフォルトで有効になっていません。複数のオリジンで CORS(クロスオリジンリソース共有)を使用している場合、Dispatcher 設定を確認し、場合によっては更新する必要があるので、デフォルトで有効にすることはできません。

NOTE
Dispatcher では Vary ヘッダーはキャッシュされません。
他の CORS 関連ヘッダーのキャッシュは、Dispatcher で有効にすることができますが、CORS オリジンが複数ある場合は不十分な可能性があります。

永続クエリのキャッシュの有効化 enable-caching-persisted-queries

永続化されたクエリのキャッシュを有効にするには、次の Dispatcher 設定ファイルの更新が必要です。

  • <conf.d/rewrites/base_rewrite.rules>

    code language-xml
    # Allow the dispatcher to be able to cache persisted queries - they need an extension for the cache file
    RewriteCond %{REQUEST_URI} ^/graphql/execute.json
    RewriteRule ^/(.*)$ /$1;.json [PT]
    
    note note
    NOTE
    Dispatcher がサフィックスを追加する .json をすべての永続化されたクエリ URL に追加し、結果をキャッシュできるようにします。
    これは、クエリが、キャッシュ可能なドキュメントに対する Dispatcher の要件に確実に従うようにするためです。 詳しくは、 Dispatcher はどのようにドキュメントを返しますか?
  • <conf.dispatcher.d/filters/ams_publish_filters.any>

    code language-xml
    # Allow GraphQL Persisted Queries & preflight requests
    /0110 { /type "allow" /method '(GET|POST|OPTIONS)' /url "/graphql/execute.json*" }
    

Dispatcher での CORS の設定 cors-configuration-in-dispatcher

CORS リクエストを使用するお客様は、Dispatcher で CORS の設定を確認および更新する必要が生じる場合があります。

  • Origin ヘッダーは、Dispatcher を介して AEM パブリッシュに渡さないでください。

    • clientheaders.any ファイルを確認します。
  • 代わりに、許可されたオリジンに対して、Dispatcher レベルで CORS リクエストを評価する必要があります。また、この方法では、CORS 関連のヘッダーが、どの場合でも 1 か所で正しく設定されます。

    • このような設定は vhost ファイルに追加されます。次に設定例を示します。簡単にするために、CORS 関連の部分のみが提供されています。特定のユースケースに合わせて調整してください。
    code language-xml
    <VirtualHost *:80>
       ServerName "publish"
    
       # ...
    
       <IfModule mod_headers.c>
           Header add X-Vhost "publish"
    
            ################## Start of the CORS specific configuration ##################
    
            SetEnvIfExpr "req_novary('Origin') == ''"  CORSType=none CORSProcessing=false
            SetEnvIfExpr "req_novary('Origin') != ''"  CORSType=cors CORSProcessing=true CORSTrusted=false
    
            SetEnvIfExpr "req_novary('Access-Control-Request-Method') == '' && %{REQUEST_METHOD} == 'OPTIONS' && req_novary('Origin') != ''  " CORSType=invalidpreflight CORSProcessing=false
            SetEnvIfExpr "req_novary('Access-Control-Request-Method') != '' && %{REQUEST_METHOD} == 'OPTIONS' && req_novary('Origin') != ''  " CORSType=preflight CORSProcessing=true CORSTrusted=false
            SetEnvIfExpr "req_novary('Origin') -strcmatch 'https://%{HTTP_HOST}*'"  CORSType=samedomain CORSProcessing=false
    
            # For requests that require CORS processing, check if the Origin can be trusted
            SetEnvIfExpr "%{HTTP_HOST} =~ /(.*)/ " ParsedHost=$1
    
            ################## Adapt the regex to match CORS origin for your environment
            SetEnvIfExpr "env('CORSProcessing') == 'true' && req_novary('Origin') =~ m#(https://.*.your-domain.tld(:\d+)?$)#" CORSTrusted=true
    
            # Extract the Origin header
            SetEnvIfNoCase ^Origin$ ^https://(.*)$ CORSTrustedOrigin=https://$1
    
            # Flush If already set
            Header unset Access-Control-Allow-Origin
            Header unset Access-Control-Allow-Credentials
    
            # Trusted
            Header always set Access-Control-Allow-Credentials "true" "expr=reqenv('CORSTrusted') == 'true'"
            Header always set Access-Control-Allow-Origin "%{CORSTrustedOrigin}e" "expr=reqenv('CORSTrusted') == 'true'"
            Header always set Access-Control-Allow-Methods "GET" "expr=reqenv('CORSTrusted') == 'true'"
            Header always set Access-Control-Max-Age 1800 "expr=reqenv('CORSTrusted') == 'true'"
            Header always set Access-Control-Allow-Headers "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers" "expr=reqenv('CORSTrusted') == 'true'"
    
            # Non-CORS or Not Trusted
            Header unset Access-Control-Allow-Credentials "expr=reqenv('CORSProcessing') == 'false' || reqenv('CORSTrusted') == 'false'"
            Header unset Access-Control-Allow-Origin "expr=reqenv('CORSProcessing') == 'false' || reqenv('CORSTrusted') == 'false'"
            Header unset Access-Control-Allow-Methods "expr=reqenv('CORSProcessing') == 'false' || reqenv('CORSTrusted') == 'false'"
            Header unset Access-Control-Max-Age "expr=reqenv('CORSProcessing') == 'false' || reqenv('CORSTrusted') == 'false'"
    
            # Always vary on origin, even if its not there.
            Header merge Vary Origin
    
            # CORS - send 204 for CORS requests which are not trusted
            RewriteCond expr "reqenv('CORSProcessing') == 'true' && reqenv('CORSTrusted') == 'false'"
            RewriteRule "^(.*)" - [R=204,L]
    
            ################## End of the CORS specific configuration ##################
    
       </IfModule>
    
       <Directory />
    
           # ...
    
       </Directory>
    
       # ...
    
    </VirtualHost>
    

AEM 用の GraphQL - 拡張機能の概要 graphql-extensions

AEM 用の GraphQL でのクエリの基本操作は、標準の GraphQL 仕様に従います。AEM での GraphQL クエリには、次のような拡張機能があります。

CORS フィルター cors-filter

CAUTION
Dispatcher のキャッシュが有効になっている場合、CORS フィルターは不要なので、このセクションは無視できます。
NOTE
AEM での CORS リソース共有ポリシーについて詳しくは、クロスオリジンリソース共有(CORS)についてを参照してください。

GraphQL エンドポイントにアクセスするには、顧客の Git リポジトリに CORS ポリシーを設定します。この設定は、1 つ以上の目的のエンドポイントに対して適切な OSGi CORS 設定ファイルを追加することで実行されます。

この設定では、アクセスを許可する必要がある、信頼できる web サイトオリジン alloworigin または alloworiginregexp を指定する必要があります。

例えば、https://my.domain の GraphQL エンドポイントと永続クエリのエンドポイントへのアクセスを許可するには、以下を使用します。

{
  "supportscredentials":true,
  "supportedmethods":[
    "GET",
    "HEAD",
    "POST"
  ],
  "exposedheaders":[
    ""
  ],
  "alloworigin":[
    "https://my.domain"
  ],
  "maxage:Integer":1800,
  "alloworiginregexp":[
    ""
  ],
  "supportedheaders":[
    "Origin",
    "Accept",
    "X-Requested-With",
    "Content-Type",
    "Access-Control-Request-Method",
    "Access-Control-Request-Headers"
  ],
  "allowedpaths":[
    "/content/_cq_graphql/global/endpoint.json",
    "/graphql/execute.json/.*"
  ]
}

エンドポイントのバニティーパスを設定した場合は、allowedpaths でも使用できます。

リファラーフィルター referrer-filter

CORS 設定に加えて、サードパーティホストからのアクセスを許可するために、リファラーフィルターを設定する必要があります。

このフィルターは、以下を行う適切な OSGi リファラーフィルター設定ファイルを追加することで実行されます。

  • 信頼できる web サイトのホスト名(allow.hosts または allow.hosts.regexp)を指定する。
  • この名前のホストに対するアクセスを許可する。

例えば、リファラー my.domain を持つリクエストにアクセスを許可するには、次の操作を行います。

{
    "allow.empty":false,
    "allow.hosts":[
      "my.domain"
    ],
    "allow.hosts.regexp":[
      ""
    ],
    "filter.methods":[
      "POST",
      "PUT",
      "DELETE",
      "COPY",
      "MOVE"
    ],
    "exclude.agents.regexp":[
      ""
    ]
}
CAUTION
以下は顧客の責任で行う必要があります。
  • 信頼できるドメインにのみアクセスを許可する
  • 機密情報が公開されていないことを確認する
  • ワイルドカードの [*] 構文を使用しない。この機能を使用すると、GraphQL エンドポイントへの認証済みアクセスが無効になり、エンドポイントが世界中に公開されることになります。
CAUTION
有効 ​になっているコンテンツフラグメントモデルから派生した)すべての GraphQL スキーマは、GraphQL エンドポイントを通じて読み取り可能です。
この機能は、このように機密データが漏洩する可能性があるため、機密データを使用できないようにする必要があることを意味します。例えば、モデル定義のフィールド名として存在する情報が含まれます。

認証 authentication

コンテンツフラグメントに対するリモート AEM GraphQL クエリの認証を参照してください。

FAQ faqs

次のような質問が寄せられました。

  1. Q:「AEM 用 GraphQL API と Query Builder API の違いは何ですか?

    • A:「AEM GraphQL API は JSON 出力の完全な制御が可能であり、コンテンツをクエリするための業界標準になっています。今後、AEM は AEM GraphQL API への投資を計画しています。

チュートリアル - AEM ヘッドレスと GraphQL をはじめる前に tutorial

実践的なチュートリアルを探している場合は、AEM ヘッドレスおよび GraphQL 入門をご覧ください。これは、AEM の GraphQL API を使用してコンテンツを構築および公開し、ヘッドレス CMS シナリオで外部アプリによって使用する方法を説明する包括的なチュートリアルです。

recommendation-more-help
19ffd973-7af2-44d0-84b5-d547b0dffee2