GraphQL アダプタの特徴
- LWCで使用可能
- Lightning Data Service対応
- ユーザーの権限(CRUD、FLS、共有)が適用される
- Apex不要
- 一回のクエリで複数の検索結果を取得可能
- クエリと同じ構造でレスポンスが返る
LWCでGraphQLを使用する
下記はGraphQLをLWCで使用したサンプルです。
import { LightningElement, api, wire } from "lwc";
import { gql, graphql, refreshGraphQL } from "lightning/uiGraphQLApi";
const QUERY = gql`
query GetFiles($recordId: ID!) {
uiapi {
query {
ContentDocumentLink(where: { LinkedEntityId: { eq: $recordId } }) {
edges {
node {
ContentDocument {
Id
Title {
value
}
FileExtension {
value
}
}
}
}
}
}
}
}
`;
export default class FileUploaderSample extends LightningElement {
@api recordId;
files = [];
wiredData;
@wire(graphql, {
query: "$recordQuery",
variables: "$variables"
})
queryGraphql(value) {
this.wiredData = value;
const { errors, data } = value;
if (data) {
const records = data.uiapi?.query?.ContentDocumentLink?.edges?.map(
(record) => record.node.ContentDocument
);
this.files = records;
} else if (errors) {
console.error("GraphQL error", errors);
}
}
get recordQuery() {
return this.recordId ? QUERY : undefined;
}
get variables() {
return { recordId: this.recordId };
}
async handleUploadFinished() {
await refreshGraphQL(this.wiredData);
this.reportValidity();
}
async handleDelete() {
await refreshGraphQL(this.wiredData);
this.reportValidity();
}
}
サンプルについて説明していきます。
まず、GraphQLのコネクタをインポートする必要があります。
import { gql, graphql, refreshGraphQL } from "lightning/uiGraphQLApi";
gqlにテンプレートリテラルを渡してクエリを宣言します。クエリに渡す変数は$varNameの形で宣言します。
なお、長くて邪魔だからと言って、外部のファイルに移すことはできません。ビルド時にテンプレートリテラルを静的に解析するため、コンパイル時にエラーとなってしまいます。
const QUERY = gql`
query GetFiles($recordId: ID!) {
uiapi {
query {
ContentDocumentLink(where: { LinkedEntityId: { eq: $recordId } }) {
edges {
node {
ContentDocument {
Id
Title {
value
}
FileExtension {
value
}
}
}
}
}
}
}
}
`;
GraphQLはwireアダプタで使用できます。
GraphQLで取得する値は、クエリとおなじ構造でdataの中に含まれます。
map関数やforEatch関数を使ってGraphQLで取得した値を画面表示で使用する形に変換します。
Apexを呼び出す時とは異なり、エラーはerrors
に格納されます(Apexの時はerror
)。
@wire(graphql, {
query: "$recordQuery",
variables: "$variables"
})
queryGraphql(value) {
this.wiredData = value;
const { errors, data } = value;
if (data) {
const records = data.uiapi?.query?.ContentDocumentLink?.edges?.map(
(record) => record.node.ContentDocument
);
this.files = records;
} else if (errors) {
console.error("GraphQL error", errors);
}
}
クエリが実行可能になるまで、Queryにundefined
を渡すことでGraphQLの実行を遅らせることができる。
get recordQuery() {
return this.recordId ? QUERY : undefined;
}
refreshGraphQL
を使用することでクエリを再実行し、GraphQLワイヤアダプタでプロビジョニングされたデータを更新できます。
async handleUploadFinished() {
await refreshGraphQL(this.wiredData);
this.reportValidity();
}
GraphQL API クライアント
GraphQL の検証にはPostmanまたはAltair GraphQLなどが利用可能です。
一度のクエリで複数の検索を実行する
GraphQL APIでは一回のクエリで10個の検索を実行することができます。
下記の例ではAccountとAccountの集計を同時に行なっています。
query MultiQuerySample {
uiapi {
query {
Account {
edges {
node {
Id
Name {
value
}
}
}
}
}
aggregate {
Account {
edges {
node {
aggregate {
Id {
count {
value
}
}
}
}
}
}
}
}
}
クエリの書き方
基本構造
「edges」はリストを表し、「node」はSObjectのレコードに相当します。
query accounts {
uiapi {
query {
Account {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
選択リスト項目のラベル、通貨項目のロケール表示を取得する
value、label、formatが個別フィールドとして返されるため、データ取得後の値変換や追加処理が不要です。
複合型項目(住所、地理位置情報)を取得する
複合型項目の取得には構成フィールドを使用します。フィルターや集計でも同様です。
親レコードの項目を取得する
最大55個の子-親リレーションを指定可能です。
最大5階層まで親の項目を取得できます。
query ParentFieldSample {
uiapi {
query {
Contact {
edges {
node {
Name { value }
AccountId { value }
Account {
Name {
value
}
}
}
}
}
}
}
}
親レコード(多態的リレーション)の項目を取得する
SOQL同様、多態的リレーションで参照先のSObjectTypeごとに取得項目を設定可能です。
多態的リレーション項目は、GraphQLではUnion型として扱われます。
特定の方のオブジェクトのフィールを参照するときにインライン・フラグメントを使用します。
インライン・フラグメントはOwner、Who、Whatの項目で使用可能です。
(他の多態的リレーションではエラーになったのでおそらく未対応です。)
... on 型名: {
フィールド名
}
ちなみにSOQLでは以下になります。
SELECT
TYPEOF Who
WHEN Contact THEN Name, Account.Name
WHEN Lead THEN Name
END
FROM Task
子レコードを取得する
最大20個の親-子リレーションを指定可能です。
最大1階層まで親の項目を取得できます。
「edges」はリストを表し、「node」はSObjectのレコードに相当します。
比較演算子を使用して検索条件を指定する
以下の形でフィルターを行います。
where: { 項目名: { 比較演算子: 値 } }
query OperatorSample {
uiapi {
query {
Contact(where: { Email: { ne: null } }) {
edges {
node {
Id
Name { value }
Email { value }
}
}
}
}
}
}
使用できる比較演算子は以下の通りです。
フィルタリングの例です。
論理演算子を使用して検索条件を組み合わせて使用する
論理演算子はAND、OR、NOTが使用できます。ネストも可能です。
日付・日時型の比較
日付・日時の比較がSOQLとは異なり、DateInputオブジェクトや日付演算子を使用します。
DateInput
日付・日時・時刻はスカラーではなく、value
、range
、literal
のフィールドを持つDateInputオブジェクトと比較します。range
はDateRange型を、literal
はDATE_LITERALを使用します。
日付演算子、日時演算子
SOQLの日付関数の代わりに、GraphQLでは日付演算子・日時演算子を使用します。
多態的リレーションの絞り込み
通常のリレーションのフィルター同様、多態的なリレーション項目でもフィルターが可能です。
位置情報による絞り込み
位置情報項目を使用することで、特定の位置から決まった距離以内のレコードを取得するというような、位置情報による絞り込みが可能です。
SELECT
Id,
Name,
Location__Latitude__s,
Location__Longitude__s
FROM
AccountWHERE
DISTANCE(Location__c, GEOLOCATION(:latitude, :longitude), 'km') < 100
WHERE句内でサブクエリを使用する(準結合・反結合)
準結合を使用する場合、inq比較演算子を使用します。
反結合の場合、ninq比較演算子を使用します。
並び替えを行う
orderByを使用して並び替えを行うことが可能です。複数キーでの並び替えもできます。
orderは、ASCで昇順、DESCで降順です。nullsは、FIRSTでnull値を先頭に、LASTで最後にします。
地理位置情報による並び替えを行う
orderByで地理情報を利用することが可能です。
SELECT
Id,
Name,
Location__Latitude__s,
Location__Longitude__s
FROM
AccountWHERE
DISTANCE(Location__c, GEOLOCATION(:latitude, :longitude), 'km') < 100
ORDER BY
DISTANCE(Location__c, GEOLOCATION(:latitude, :longitude), 'km')
ページング
LIMIT・OFFSETの代わりにGraphQLではfirst/afterをつかって、カーソルベースのページングを行います。
RecordQueryタイプのfirst、after、upperBoundを使用してクエリ結果をページングします。
- first: 1ページあたりのレコード数を200〜2000の間で指定する
- after: 2ページ以降のカーソル位置を指定する
- upperBound: 取得するレコードの上限を指定する
endCursorで最後の行のカーソルが取得できます。
集計結果を取得する
UIAPIタイプにqueryのかわりにaggregateを使用します。
使用できる集計フィールドは以下になります。