スキーマ.Graphql , TypeScriptおよびヘッドレスワードプレスからのorgオブジェクト


怠け者になりましょう


Here's a repo with a basic setup WPGraphSQLからスキーマ内のJSONリンクされたデータにマップされます.タイプセーフ機能を介してorgの語彙.それが複雑であるならば、長い説明です

スキーマへのイントロ。org


より簡単に検索エンジンで発見すること.WebSite sは、彼らの内容に構造化データを加えることができます.このようにGoogleの検索サイトの独自のRating s

しかし、それはウェブをよりインタラクティブにするのにも用いられます.メール可能なデータを含めることができますAction これはGmailがこの“Look pull request”ボタンになったので、メールのリンクを開いて、読んでクリックする必要はありません.

上の例は、一般的な、オープンな語彙、スキーマを使用しています.したがって、他の電子メールサービスはGithubの電子メールの構造データを理解することができました.さらに重要なことは、我々は自分のWebページにJSON LDと他の形式で構造化されたデータを追加することができますEvent 我々は彼らの近くにホストしています

私はどのように多くの読者がイベントをホストしているかわからないが、私はそれがイベントホストが動的にデータを、例えばWordPressと記入するように簡単になると思います.

それのためのプラグインがありませんか?


短い答えはYes, many . この記事はGraphSQLで動作するTypeScript開発者向けです.私たちはWordPressをヘッドレスコンテンツ管理システム(CMS)として使用します.この記事はPHPの1行も含みません.代わりに、GraphSQLスキーマからtypescriptを生成します.このアプローチは、実際にWordPressなしで動作しますが、それを開始するのは簡単です.

WordPressからGrapQエンドポイントまで


The WPGraphQL Plugin すぐにあなたのWordPressインストールの管理パネルからインストールされます.

アドミンパネルを残して質問をする必要はありません.

その質問は、特定のカテゴリ、短い抜粋とフィーチャーされたイメージのすべてのポストを検索します.

GraphSQLの終点からGraphSQLスキーマへ


今、これはセキュリティ関連です:私たちがGraphSQL APIを持っているとき、それはいわゆるintrospection query . GraphSQLスキーマへのアクセスがない場合は、そのようなクエリを使用してGraphSQLスキーマを生成できます.攻撃者は同じことをすることができて、あなたのAPIで脆弱性を見つけるようにしようとします.WPGraphqlはデフォルトでオフになっています.

それがオンになっている間、我々はgraphql パッケージとnode.js スキーマを生成し、ローカルに保存します.
import fs from "fs";
import fetch from "node-fetch";
import {
    getIntrospectionQuery,
    printSchema,
    buildClientSchema,
} from "graphql";

/**
 * runs an introspection query on an endpoint and retrieves its result
 * thanks to this gist:
 * https://gist.github.com/craigbeck/b90915d49fda19d5b2b17ead14dcd6da
 */
async function main() {
    const introspectionQuery = getIntrospectionQuery();
    const response = await fetch("https://blog.example.com/graphql", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({ query: introspectionQuery }),
    });
    const { data } = await response.json();
    const schema = buildClientSchema(data);
    const outputFile = "./wpgraphql-schema.gql";
    fs.writeFileSync(outputFile, printSchema(schema));
}

main();
置換blog.example.com あなたのドメインまたはlocalhostで、あなたのgraphql終点がどこで見つけることができます.そのスクリプトを実行するには"type": "module" あなたのパッケージで.JSON ( ES 6構文のため).
そして、あなたはwpgraphql-schema.gql ファイルをお願いします.

グラフィカルSQLスキーマから


wpgraphql-schema.gql ファイルは既にタイプスクリプトタイプを作成できます.そのためには、GraphSQLジェネレータをプロジェクトに追加します.yarn add @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations -Dジェネレータを実行するには、少しの設定が必要ですcodegen.yml -ファイル
overwrite: true
schema: "./wpgraphql-schema.gql"
documents: "src/**/*.graphql"
generates:
  src/generated/wp-graphql.ts:
    plugins:
      - "typescript"
      - "typescript-operations"
最初の行は既存の上書きを指示します.TSファイル、2番目のポイントは、私たちがエンドポイントから得たGraphSQLスキーマを指していますglob-pattern or file where our queries, mutations, fragments and subscriptions can be found . 「ドキュメント」を持っていないなら、まだジェネレータを実行できます.yarn graphql-codegen --config codegen.ymlGraphSQLスキーマの型を取得します.したがって、すべてのGraphSQLスキーマ型を取得するにはplugins: - "typescript" パート.クエリの取得などは- "typescript-operations" パート.それは私たちに見つけるすべての“ドキュメント”の種類を追加します.tsファイル.また、別の生成することができます.これは、クエリやフラグメントなどのクライアント機能からサーバー機能を分割するためです.
WPGraphqlを使用することができました.Post -へのマッパー関数を作成する型schema.org/BlogPosting . しかし、その型はかなり大きく、特定のブログ記事のすべてのフィールドを常に問い合わせることはできません.
ご覧のように、WPGraphqlの型についてドキュメントを記述する必要はありません.
クエリを簡単にするには、以下のようなGraphSQLフラグメントを作成できます.
fragment PostPreview on Post {
  title
  excerpt
  featuredImage {
    node {
      sourceUrl
      altText
      description(format: RAW)
      srcSet(size: THUMBNAIL)
    }
  }
  slug
}
そして今、これらの正確な同じフィールドを問い合わせたいときは、クエリで参照できるだけです.
query wpPostPreviewByCategory {
  posts(where: { categoryName: "my-side-projects" }) {
    edges {
      node {
        ...PostPreview
      }
    }
  }
}
このクエリは、特定のカテゴリからの投稿を取得しますが、各ポストのフラグメントからのフィールドのみを取得します.これは、グラフィカルなクエリで再利用性に最適ですgraphql-codegen 型を生成するPostPreviewFragment 私たちにとっては、一貫してクエリ全体で動作するマッパー関数を作成できます.
マッパー関数から始めることができます.
import { PostPreviewFragment } from "./../generated/wp-graphql";
//other imports

export function mapWpPostPreviewToSchemaBlogPost(
    input: PostPreviewFragment,
    wpBaseURL?: string
): BlogPosting {
    // ...curious about wpBaseURL and BlogPosting? We'll use that to build our json-ld
}

タイプスクリプトからスキーマへ。org


こちらですa good example of what a Blogposting can look like JSON LDと他の形式でHTMLと混ざり合う.JSON LDによるスクリプトタグの追加の利点は、私たちがウェブサイトを生成できるということです.これは、特にいくつかのスクリプトのタグを追加することが許可されているので、特に柔軟性を与えるtype="application/ld+json" Webページに.
もちろん、我々のウェブサイトの人間の読者のために、我々は何か視覚的で欲しいです.我々のブログ記事のために、我々はリンク、イメージ、ポストのタイトルと短いプレビューを表示するかもしれません.我々のウェブサイトを読んでいる機械は、どちらが何であるかについて、わかりません.ブログ投稿として適切に読むのを助けるために、JSON LDは以下のようになります.
<script type="application/ld+json">
{
  "@context":"https://schema.org",
  "@type": "BlogPosting",
  "@id": "https://shnyder.com/business-model-canvas-for-metaexplorer",
  "name": "Business Model Canvas for MetaExplorer",
  "abstract": "<p>Before starting out with my plans to turn metaexplorer into a business, I wrote a business plan &#8211; and because 2020 showed us what it thinks of plans&#8230;</p>\n",
  "image": {
    "@type": "ImageObject",
    "name": "Metaexplorer business model canvas, 2020-12-28",
    "contentUrl": "https://shnyder.com/wp-content/uploads/2020/12/IMG_20201228_234552-scaled.jpg",
    "thumbnailUrl": "https://shnyder.com/wp-content/uploads/2020/12/IMG_20201228_234552-150x150.jpg"
  }
}
</script>
例のようにわかるように"@id" -フィールドとイメージのURLは、ドメイン名-私のブログのドメインが含まれます.相対的なURLもJSON LDのように、shnyder上で可能です.私はこれを使用できます."@id": "/business-model-canvas-for-metaexplorer"ここでdev . toに完全なドメイン名を入れたいと思います.マッピング関数にマイドメインを含めるには、オプションのパラメータを追加しますwpBaseURL .
では、どのようにしてJSON LDをGraphSQL型から取得しますか?図書館schema-dts 我々が正確にそれをするのを助けてください、そして、typesafe方法で.それは、我々がIntellisenseを得るということですactual schema.org vocabulary ( ontology , である).

以下のようにインストールできます:yarn add schema-dtsナイーブなアプローチは、GlenqlからJSON LDまでの「幸せなパス」だけを写像することです、そこで、我々は我々が必要とするデータを常に持ちます.このアプローチは次のようになります.
import { PostPreviewFragment } from "./../generated/wp-graphql";
import { BlogPosting } from "schema-dts";

/**
 * mapper-function to create schema.org/BlogPosting(s) for previews from fragments
 * @param input a fragment of a WordPress blog post
 * @param wpBaseURL the base domain of your wordpress installation. Used to add the slug
 */
export function mapWpPostPreviewToSchemaBlogPost(
    input: PostPreviewFragment,
    wpBaseURL?: string
): BlogPosting {
    let featuredImgNode = input.featuredImage.node;
    let thumbnailUrl = featuredImgNode.srcSet
        .split(" ")
        .find((val) => val.startsWith("http://"));
    let output: BlogPosting = {
        "@type": "BlogPosting",
        ...() => {wpBaseURL ? {"@id":`${wpBaseURL}/${input.slug}`} : undefined},
        name: input.title,
        abstract: input.excerpt,
        image: {
            "@type": "ImageObject",
            description: featuredImgNode.description,
            name: featuredImgNode.altText,
            contentUrl: featuredImgNode.sourceUrl,
            thumbnailUrl
        }
    };
    return output;
}
そして、これは標準的なタイプスクリプト構成でコンパイルします.ことは、我々は常にプレビュー画像を取得することはできません、他のフィールドも同様に空かもしれないことです.空のフィールドを扱うことは将来のエラーを防ぐでしょう.マッパー関数では設定する必要がありますcompilerOptions.strict to true 私たちのtsconfigで.JSONもう一度コンパイルしようとすると、以下のようなエラーがたくさんあります.- error TS2533: Object is possibly 'null' or 'undefined'.andType 'null' is not assignable to type 'string | PronounceableTextLeaf | readonly PronounceableText[] | undefined'.我々は、何かのようにしたくないので"title": undefined JSON LDでは、出力オブジェクトからフィールドを取り除きます.これを修正する最初の考えはif文をたくさん追加することです.しかし最近のES 6とTypesScriptの進歩により、入れ子JSON LDオブジェクトのように見えます.このようなネストしたオブジェクトは出力したいものです.主な違いは、この入れ子になったオブジェクトにも安全なマッピングの条件が含まれています.
/**
 * mapper-function to create schema.org/BlogPosting(s) for previews from fragments
 * @param input a fragment of a WordPress blog post
 * @param wpBaseURL the base domain of your wordpress installation. Used to add the slug
 */
export function mapWpPostPreviewToSchemaBlogPost(
    input?: PostPreviewFragment,
    wpBaseURL?: string
): BlogPosting | null {
    if (!input) return null;
    let featuredImgNode = input && input.featuredImage && input.featuredImage.node;
    let thumbnailUrl = featuredImgNode && featuredImgNode.srcSet && featuredImgNode
        .srcSet.split(" ")
        .find((val) => val.startsWith("http"));
    let output: BlogPosting = {
        "@type": "BlogPosting",
        ...(wpBaseURL && { "@id": `${wpBaseURL}/${input.slug}` }),
        ...(input.title && { name: input.title }),
        ...(input && input.excerpt && { abstract: input.excerpt }),
        ...(featuredImgNode && {
            image: {
                "@type": "ImageObject",
                ...(featuredImgNode.description && {
                    description: featuredImgNode.description,
                }),
                ...(featuredImgNode.altText && {
                    name: featuredImgNode.altText,
                }),
                ...(featuredImgNode.sourceUrl && {
                    contentUrl: featuredImgNode.sourceUrl,
                }),
                ...(thumbnailUrl && { thumbnailUrl }),
            },
        }),
    };
    return output;
}
23行は、厳密なチェックで32行に比較して、NULLチェックを行わない.さらに、この表記法は入れ子構造にスケールする.多くの余分な努力-しかし、信頼性は大幅に改善されています.それをコードの詳細を見てみましょう.最初に、出力はオブジェクトタイプの「ブログ投稿」です、そして、それはAを含まなければなりません"@type": "BlogPosting" . タイプスクリプトタイプはコンパイル中に失われます"@type" JSONに焼きます
let output: BlogPosting = { "@type": "BlogPosting" };
機能の向こう側に現れる3つのドットはspread operators . それらの演算子は評価結果をブラケットに広げるようにしています() .
...(input.title && { name: input.title }),
ブラケットの中には論理演算子があり、これは第2の部分を返すif the first one is truthy , そうでなければ最初のもの.つまり、入力フィールドの1つがinput.title が定義されていない場合は、空の文字列あるいは空の文字列を返します.
let mytestvar = {...undefined};
console.log(mytestvar);
mytestvar = {...null};
console.log(mytestvar);
mytestvar = {...""};
console.log(mytestvar);
mytestvar = {...false};
console.log(mytestvar);
mytestvar = {...true};
console.log(mytestvar);
//always an object with no fields
変換のこの方法は、文字列ベースの値を確保するのに役立ちますが、API変換のための魔法の構文ではありません.
しかし、我々があるならばinput.title , 第二の式{ name: input.title } がオブジェクトであり、そのキーと値が含まれているオブジェクトに追加されます.

結論


今、あなたはあなたのGraphSQL APIから構造化データを構築するパイプラインを持っています.構造化されたデータは、SEOに良い効果を持つことができます、いくつかは、Googleの特別なウィジェットとして表示されます.With schema-dts 構造化されたデータを構築している間、Typosを避けることはできません.IDEは、あなたの@type s.
あなたが建築のそれらの転換をどこに置くべきかについて考えているならばmiddleman engine も全体として組織内のデータの処理を改善することができます.