API駆動のWebサイトが世界を旅するのにどのように役立つか
公開: 2022-03-10(これは後援された投稿です。)最近、私は個人のWebサイトを再構築することにしました。それは、6年前であり、丁寧に言えば、少し「時代遅れ」に見えたからです。 目標は、自分自身、ブログエリア、最近のサイドプロジェクトのリスト、および今後のイベントに関する情報を含めることでした。
私は時々クライアントの仕事をしているので、扱いたくないことが1つありました。それはデータベースです! 以前、私は私を望んでいたすべての人のためにWordPressサイトを構築しました。 プログラミングの部分は通常私にとっては楽しいものでしたが、リリース、データベースのさまざまな環境への移動、実際の公開は常に面倒でした。 安価なホスティングプロバイダーは、MySQLデータベースをセットアップするための貧弱なWebインターフェイスしか提供しておらず、ファイルをアップロードするためのFTPアクセスは常に最悪の部分でした。 私の個人的なウェブサイトではこれに対処したくありませんでした。
したがって、再設計の要件は次のとおりです。
- JavaScriptとフロントエンドテクノロジーに基づく最新のテクノロジースタック。
- どこからでもコンテンツを編集するためのコンテンツ管理ソリューション。
- パフォーマンスが高く、結果が速いサイト。
この記事では、私が構築したものと、私のWebサイトが驚くほど私の毎日の仲間であることがどのように判明したかを示したいと思います。
コンテンツモデルの定義
ウェブ上で物事を公開するのは簡単なようです。 必要なすべてのページにWYSIWYGエディター( What Y ou S ee I s W hat Y ou G et )を提供するコンテンツ管理システム(CMS)を選択すると、すべてのエディターがコンテンツを簡単に管理できます。 それだけですよね?
小さなカフェから成長中のスタートアップに至るまで、いくつかのクライアントWebサイトを構築した後、聖なるWYSIWYGエディターが必ずしも私たち全員が探している特効薬ではないことがわかりました。 これらのインターフェースは、Webサイトの構築を容易にすることを目的としていますが、要点は次のとおりです。
ウェブサイトの構築は簡単ではありません
Webサイトのコンテンツを絶えず壊さずに構築および編集するには、HTMLについての深い知識があり、少なくともCSSについて少し理解している必要があります。 それはあなたがあなたの編集者に期待できるものではありません。
WYSIWYGエディターで構築された恐ろしい複雑なレイアウトを見てきましたが、システムが壊れやすいためにすべてが崩壊したときのすべての状況に名前を付けることはできません。 これらの状況は、すべての当事者が避けられない何かについてお互いを非難している戦いと不快感につながります。 私は常にこれらの状況を避け、編集者が怒りのメールを叫ぶのを避けるために快適で安定した環境を作るように努めました。 すべてが壊れています。」
構造化されたコンテンツはあなたにいくつかのトラブルを節約します
必要なすべてのWebサイトのコンテンツをいくつかのチャンクに分割し、それぞれが表現を考えずに相互に関連している場合、人々が物事を壊すことはめったにないことをすぐに学びました。 WordPressでは、これはカスタム投稿タイプを使用して実現できます。 各カスタム投稿タイプには、独自のわかりやすいテキストフィールドを持つ複数のプロパティを含めることができます。 思考の概念をページに完全に埋め込んだ。
私の仕事は、コンテンツを接続し、これらのコンテンツブロックからWebページを構築することでした。 これは、編集者が自分のWebサイトで視覚的な変更を行うことができたとしてもほとんどできないことを意味しました。 彼らはコンテンツに責任があり、コンテンツのみに責任がありました。 視覚的な変更は私が行う必要がありました。誰もがサイトのスタイルを設定できるわけではなく、脆弱な環境を回避できました。 この概念は大きなトレードオフのように感じられ、通常は好評でした。
後で、私がやっていることはコンテンツモデルを定義することであることに気づきました。 Rachel Lovingerは、優れた記事「コンテンツモデリング:マスタースキル」で、コンテンツモデルを次のように定義しています。
「コンテンツモデルは、特定のプロジェクトで使用するさまざまな種類のコンテンツをすべて文書化します。 各コンテンツタイプの要素とそれらの相互関係の詳細な定義が含まれています。」
コンテンツモデリングから始めて、1つを除いてほとんどのクライアントでうまく機能しました。
「ステファン、私はあなたのデータベーススキーマを定義していません!」
この1つのプロジェクトのアイデアは、いくつかの異なるページや場所に表示されるすべてのバリエーションで、大量のコンテンツを提供することにより、多くの有機的なトラフィックを作成する必要がある大規模なWebサイトを構築することでした。 このプロジェクトに取り組むための戦略について話し合うための会議を設定しました。
含める必要のあるすべてのページとコンテンツモデルを定義したかったのです。 クライアントがどの小さなウィジェットやサイドバーを念頭に置いているかは関係ありませんでした。明確に定義したかったのです。 私の目標は、編集者に使いやすいインターフェイスを提供し、考えられる形式で表示するための再利用可能なデータを提供できる、堅実なコンテンツ構造を作成することでした。
結局、このプロジェクトのアイデアはあまり明確ではなく、すべての質問に対する答えを得ることができませんでした。 プロジェクトリーダーは、適切なコンテンツモデリング(設計と開発ではない)から始めるべきであることを理解していませんでした。 彼にとって、これはほんの1トンのページでした。 大量のテキストを追加するための重複したコンテンツと巨大なテキスト領域は、問題ではないようでした。 彼の考えでは、私が構造について持っていた質問は技術的なものであり、彼らはそれらについて心配する必要はありません。 簡単に言うと、私はプロジェクトを行いませんでした。
重要なのは、コンテンツモデリングはデータベースに関するものではないということです。
それはあなたのコンテンツをアクセス可能で将来にわたって利用できるようにすることです。 プロジェクトのキックオフ時にコンテンツのニーズを定義しないと、後でそれを再利用することは、不可能ではないにしても、非常に困難になります。
適切なコンテンツモデリングは、現在および将来のWebサイトの鍵です。
満足のいく:ヘッドレスCMS
自分のサイトでも優れたコンテンツモデリングをフォローしたいと思ったことは明らかでした。 しかし、もう1つありました。 新しいWebサイトを構築するためにストレージレイヤーを扱いたくなかったので、現在取り組んでいるヘッドレスCMSであるContentfulを使用することにしました(完全な免責事項!)。 「ヘッドレス」とは、このサービスがクラウド内のコンテンツを管理するためのWebインターフェイスを提供し、データをJSON形式で返すAPIを提供することを意味します。 このCMSを選択すると、APIを数分で利用でき、インフラストラクチャのセットアップを行う必要がなかったため、すぐに生産性を高めることができました。 Contentfulは、私の個人的なWebサイトのような小さなプロジェクトに最適な無料プランも提供しています。
すべてのブログ投稿を取得するためのクエリ例は次のようになります。
<a href="https://cdn.contentful.com/spaces/space_id/entries?access_token=access_token&content_type=post">https://cdn.contentful.com/spaces/space_id/entries?access_token=access_token&content_type=post</a>
そして、短縮版の応答は次のようになります。
{ "sys": { "type": "Array" }, "total": 7, "skip": 0, "limit": 100, "items": [ { "sys": { "space": {...}, "id": "455OEfg1KUskygWUiKwmkc", "type": "Entry", "createdAt": "2016-07-29T11:53:52.596Z", "updatedAt": "2016-11-09T21:07:19.118Z", "revision": 12, "contentType": {...}, "locale": "en-US" }, "fields": { "title": "How to React to Changing Environments Using matchMedia", "excerpt": "...", "slug": "how-to-react-to-changing-environments-using-match-media", "author": [...], "body": "...", "date": "2014-12-26T00:00+02:00", "comments": true, "externalUrl": "https://4waisenkinder.de/blog/2014/12/26/handle-environment-changes-via-window-dot-matchmedia/" }, {...}, {...}, {...}, {...}, {...}, {...} ] } }
Contentfulの素晴らしいところは、私が必要としていたコンテンツモデリングに優れていることです。 提供されているWebインターフェイスを使用して、必要なすべてのコンテンツをすばやく定義できます。 Contentfulでの特定のコンテンツモデルの定義は、コンテンツタイプと呼ばれます。 ここで指摘するのは、コンテンツアイテム間の関係をモデル化する機能です。 たとえば、著者とブログ投稿を簡単に結び付けることができます。 これにより、構造化されたデータツリーが作成され、さまざまなユースケースで再利用するのに最適です。
そこで、将来構築したいページを考えずにコンテンツモデルを設定しました。
次のステップは、このデータで何をしたいのかを理解することでした。 知っているデザイナーに聞いてみると、次のような構成のホームページのインデックスページを思いついた。
Node.jsを使用したHTMLページのレンダリング
今、トリッキーな部分が来ました。 これまでのところ、ストレージやデータベースを扱う必要はありませんでした。これは私にとって大きな成果でした。 では、APIしか利用できない場合、どうすればWebサイトを構築できますか?
私の最初のアプローチは、日曜大工のアプローチでした。 データを取得してそこからHTMLをレンダリングする単純なNode.jsスクリプトを書き始めました。
すべてのHTMLファイルを事前にレンダリングすることで、私の主な要件の1つが満たされました。 静的HTMLは非常に高速に提供できます。
それでは、私が使用したスクリプトを見てみましょう。
'use strict'; const contentful = require('contentful'); const template = require('lodash.template'); const fs = require('fs'); // create contentful client with particular credentials const client = contentful.createClient({ space: 'your_space_id', accessToken: 'your_token' }); // cache templates to not read // them over and over again const TEMPLATES = { index : template(fs.readFileSync(`${__dirname}/templates/index.html`)) }; // fetch all the data Promise.all([ // get posts client.getEntries({content_type: 'content_type_post_id'}), // get events client.getEntries({content_type: 'content_type_event_id'}), // get projects client.getEntries({content_type: 'content_type_project_id'}), // get talk client.getEntries({content_type: 'content_type_talk_id'}), // get specific person client.getEntries({'sys.id': 'person_id'}) ]) .then(([posts, events, projects, talks, persons]) => { const renderedHTML = TEMPLATES.index({ posts, events, projects, talks, person : persons.items[0] }) fs.writeFileSync(`${__dirname}/build/index.html`, renderedHTML); console.log('Rendered HTML'); }) .catch(console.error);
<!doctype html> <html lang="en"> <head> <!-- ... --> </head> <body> <!-- ... --> <h2>Posts</h2> <ul> <% posts.items.forEach( function( talk ) { %> <li><%- talk.fields.title %> <% }) %> </ul> <!-- ... --> </body> </html>
これはうまくいきました。 ファイルの構造と機能に関するすべての決定を行い、完全に柔軟な方法で目的のWebサイトを構築できました。 完全に異なるデータセットを使用して異なるページタイプをレンダリングすることは、まったく問題ありませんでした。 HTMLレンダリングに付属している既存のCMSのルールや構造と戦ってきた人なら誰でも、完全な自由が素晴らしいことであることを知っています。 特に、データモデルが時間の経過とともに多くの関係を含めてより複雑になると、柔軟性が報われます。
このNode.jsスクリプトでは、Contentful SDKクライアントが作成され、クライアントメソッドgetEntries
を使用してすべてのデータがフェッチされます。 クライアントに提供されるすべてのメソッドはpromise駆動型であるため、深くネストされたコールバックを簡単に回避できます。 テンプレートには、lodashのテンプレートエンジンを使用することにしました。 最後に、ファイルの読み取りと書き込みのために、Node.jsはネイティブのfs
モジュールを提供します。このモジュールは、テンプレートの読み取りとレンダリングされたHTMLの書き込みに使用されます。
ただし、このアプローチには1つの欠点がありました。 それは非常に骨の折れるものでした。 この方法が完全に柔軟であったとしても、車輪の再発明のように感じました。 私が構築していたのは基本的に静的サイトジェネレーターで、すでにたくさんあります。 もう一度やり直す時が来ました。
実際の静的サイトジェネレーターを探す
JekyllやMiddlemanなどの有名な静的サイトジェネレーターは、通常、HTMLにレンダリングされるMarkdownファイルを処理します。 編集者はこれらを操作し、WebサイトはCLIコマンドを使用して構築されます。 しかし、このアプローチは私の最初の要件の1つに失敗していました。 プライベートコンピュータにあるファイルに頼らずに、どこにいてもサイトを編集できるようにしたかったのです。
私の最初のアイデアは、APIを使用してこれらのMarkdownファイルをレンダリングすることでした。 これはうまくいったでしょうが、それは正しく感じられませんでした。 後でHTMLに変換するためにMarkdownファイルをレンダリングすることは、私の最初のソリューションと比較して大きなメリットを提供しない2つのステップでした。
幸いなことに、MetalsmithやMiddlemanなどのContentful統合があります。 このプロジェクトではMetalsmithを選択しました。これは、Node.jsで記述されているため、Rubyの依存関係を持ち込みたくなかったためです。
Metalsmithは、ソースフォルダーからファイルを変換し、宛先フォルダーにレンダリングします。 これらのファイルは、必ずしもMarkdownファイルである必要はありません。 Sassのトランスパイルや画像の最適化にも使用できます。 制限はなく、とても柔軟です。
Contentful統合を使用して、構成ファイルとして取得され、APIから必要なすべてをフェッチできるいくつかのソースファイルを定義することができました。
--- title: Blog contentful: content_type: content_type_id entry_filename_pattern: ${ fields.slug } entry_template: article.html order: '-fields.date' filter: include: 5 layout: blog.html description: >- Recent articles by Stefan Judis. ---
この設定例では、APIリクエストの応答を含む親blog.html
ファイルを使用してブログ投稿領域をレンダリングしますが、 article.html
テンプレートを使用していくつかの子ページもレンダリングします。 子ページのファイル名は、 entry_filename_pattern
を介して定義されます。
ご覧のとおり、このようなものを使用すると、ページを簡単に作成できます。 この設定は、すべてのページがAPIに依存していることを確認するために完全に機能しました。
サービスをプロジェクトに接続する
唯一欠けていたのは、サイトをCMSサービスに接続し、コンテンツが編集されたときにサイトを再レンダリングすることでした。 この問題の解決策— GitHubなどのサービスを使用している場合は、すでにおなじみのWebhookです。
Webhookは、何かが起こったことを通知する、以前に定義されたエンドポイントへのサービスとしてのソフトウェアによって行われる要求です。 たとえば、GitHubは、誰かがリポジトリの1つでプルリクエストを開いたときにpingを返すことができます。 コンテンツ管理に関しては、ここでも同じ原則を適用できます。 コンテンツに何かが起こったときはいつでも、エンドポイントにpingを実行し、特定の環境がそれに反応するようにします。 私たちの場合、これは、metalsmithを使用してHTMLを再レンダリングすることを意味します。
Webhookを受け入れるために、JavaScriptソリューションも使用しました。 私が選んだホスティングプロバイダー(Uberspace)を使用すると、Node.jsをインストールして、サーバー側でJavaScriptを使用できます。
const http = require('http'); const exec = require('child_process').exec; const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); // check for secret header // to not open up this endpoint for everybody if (req.headers.secret === 'YOUR_SECRET') { res.end('ok'); // wait for the CDN to // invalidate the data setTimeout(() => { // execute command exec('npm start', { cwd: __dirname }, (error) => { if (error) { return console.log(error); } console.log('Rebuilt success'); }); }, 1000 * 120 ); } else { res.end('Not allowed'); } }); console.log('Started server at 8000'); server.listen(8000);
このスクリプトは、ポート8000で単純なHTTPサーバーを起動します。着信要求をチェックして適切なヘッダーを探し、それがContentfulからのWebhookであることを確認します。 リクエストがWebhookとして確認されると、事前定義されたコマンドnpm start
が実行され、すべてのHTMLページが再レンダリングされます。 なぜタイムアウトがあるのか不思議に思うかもしれません。 これは、保存されたデータがCDNから提供されるため、クラウド内のデータが無効になるまでアクションを一時停止するために必要です。
環境によっては、このHTTPサーバーにインターネットからアクセスできない場合があります。 私のサイトはApacheサーバーを使用して提供されているため、実行中のノードサーバーをインターネットにアクセスできるようにするために内部書き換えルールを追加する必要がありました。
# add node endpoint to enable webhooks RewriteRule ^rerender/(.*) https://localhost:8000/$1 [P]
API-最初の構造化データ:永遠の親友
この時点で、私はクラウド内のすべてのデータを管理することができ、私のWebサイトは変更後にそれに応じて反応しました。
あちこちでの繰り返し
外出中は私の人生の重要な部分であるため、特定の会場の場所や予約したホテルなどの情報をすぐに利用できるようにする必要がありました。通常はGoogleスプレッドシートに保存されています。 今では、情報はスプレッドシート、いくつかの電子メール、私のカレンダー、そして私のWebサイトに広がっていました。
私は認めざるを得ませんでした。私は毎日の流れの中で多くのデータ重複を作成しました。
構造化データの瞬間
私は、信頼できる唯一の情報源(できれば電話で)を夢見て、どのイベントが予定されているかをすばやく確認するだけでなく、ホテルや会場に関する追加情報も入手しました。 私のWebサイトにリストされているイベントには、現時点ではすべての情報が含まれていませんでしたが、Contentfulのコンテンツタイプに新しいフィールドを追加するのは非常に簡単です。 そこで、必要なフィールドを「イベント」コンテンツタイプに追加しました。
この情報を私のウェブサイトCMSに入れることは、オンラインで表示されるべきではないため、私の意図ではありませんでしたが、APIを介してアクセスできるようにすることで、このデータでまったく異なることができるようになりました。
JavaScriptを使用したネイティブアプリの構築
モバイル向けのアプリの構築は何年も前から話題になっていますが、これにはいくつかのアプローチがあります。 プログレッシブウェブアプリ(PWA)は、最近特に注目されているトピックです。 Service WorkerとWebアプリマニフェストを使用すると、ホーム画面のアイコンからWebテクノロジーを使用した管理されたオフライン動作に至るまで、アプリのような完全なエクスペリエンスを構築できます。
言及すべき欠点が1つあります。 プログレッシブウェブアプリは増加していますが、まだ完全には存在していません。 たとえば、Service Workerは現在Safariでサポートされておらず、これまでのところApple側からのみ「検討中」です。 iPhoneにもオフライン対応のアプリが欲しかったので、これは私にとって大きな問題でした。
そこで私は代替案を探しました。 私の友人は本当にNativeScriptに夢中になっていて、このかなり新しいテクノロジーについて私に話し続けました。 NativeScriptは、JavaScriptを使用して真にネイティブなモバイルアプリを構築するためのオープンソースフレームワークであるため、試してみることにしました。
NativeScriptを理解する
ネイティブモバイル環境向けに開発するには多くのものをインストールする必要があるため、NativeScriptのセットアップには時間がかかります。 npm install nativescript -g
を使用してNativeScriptコマンドラインツールを初めてインストールするときに、インストールプロセスをガイドします。
次に、scaffoldコマンドを使用して新しいプロジェクトを設定tns create MyNewApp
しかし、これは私がしたことではありません。 ドキュメントをスキャンしていて、NativeScriptで構築されたサンプルの食料品管理アプリに出くわしました。 そこで、このアプリを使ってコードを掘り下げ、ニーズに合わせて段階的に変更しました。
プロセスを深く掘り下げたくはありませんが、必要なすべての情報を含む見栄えのするリストを作成するのに、それほど時間はかかりませんでした。
NativeScriptは、Angular 2と非常にうまく連携します。これは、NativeScript自体が十分に大きいと感じたため、今回は試したくありませんでした。 NativeScriptでは、「ビュー」を作成する必要があります。 各ビューは、基本レイアウトとオプションのJavaScriptおよびCSSを定義するXMLファイルで構成されています。 これらはすべて、ビューごとに1つのフォルダーで定義されます。
単純なリストのレンダリングは、次のようなXMLテンプレートを使用して実現できます。
<!-- call JavaScript function when ready --> <Page loaded="loaded"> <ActionBar title="All Travels" /> <!-- make it scrollable when going too big --> <ScrollView> <!-- iterate over the entries in context --> <ListView items="{{ entries }}"> <ListView.itemTemplate> <Label text="{{ fields.name }}" textWrap="true" class="headline"/> </ListView.itemTemplate> </ListView> </ScrollView> </Page>
ここで最初に発生するのは、ページ要素の定義です。 このページの中で、私はActionBar
を定義して、クラシックなAndroidの外観と適切な見出しを付けました。 ネイティブ環境向けに物を構築するのは、少し難しい場合があります。 たとえば、スクロール動作を機能させるには、「ScrollView」を使用する必要があります。 最後に、 ListView
を使用してイベントを繰り返し処理します。 全体的に、それはかなり簡単に感じました!
しかし、ビューで使用されているこれらのエントリはどこから来ているのでしょうか。 そのために使用できる共有コンテキストオブジェクトがあることがわかりました。 ビューのXMLを読んでいるときに、ページにloaded
た属性セットがあることにすでに気付いているかもしれません。 この属性を設定することで、ページが読み込まれたときに特定のJavaScript関数を呼び出すようにビューに指示します。
このJavaScript関数は、依存するJSファイルで定義されています。 exports.something
を使用してエクスポートするだけで、アクセス可能にすることができます。 データバインディングを追加するには、ページプロパティbindingContext
に新しいObservableを設定するだけです。 NativeScriptのObservablesは、 'iews内のデータ変更に反応するために必要なpropertyChange
イベントを発行しますが、そのままで機能するため、それについて心配する必要はありません。
const context = new Observable({ entries: null}); const fetchModule = require('fetch'); // export loaded to be called from // List.xml when everything is loaded exports.loaded = (args) => { const page = args.object; page.bindingContext = context; fetchModule.fetch( `https://cdn.contentful.com/spaces/${config.space}/entries?access_token=${config.cda.token}&content_type=event&order=fields.start`, { method: "GET", headers: { 'Content-Type': 'application/json' } } ) .then(response => response.json()) .then(response => context.set('entries', response.items)); }
最後に、データをフェッチしてコンテキストに設定します。 これは、NativeScript fetch
モジュールを使用して実行できます。 ここで、結果を確認できます。
したがって、ご覧のとおり、NativeScriptを使用して単純なリストを作成することはそれほど難しくありません。 その後、別のビューと、イベントのWebサイトを表示するためにGoogleマップとWebビューで特定のアドレスを開くための追加機能を使用してアプリを拡張しました。
ここで指摘することの1つは、NativeScriptはまだかなり新しいことです。つまり、npmにあるプラグインには、通常、GitHubに多くのダウンロードやスターがありません。 これは最初はイライラしましたが、いくつかのネイティブコンポーネント(nativescript-floatingactionbutton、nativescript-advanced-webview、nativescript-pulltorefresh)を使用して、ネイティブエクスペリエンスを実現し、すべてが完全に正常に機能しました。
ここで改善された結果を見ることができます:
このアプリに追加する機能が多ければ多いほど、それが好きになり、使用するようになりました。 最良の部分は、データの重複をなくし、データをすべて1か所で管理しながら、さまざまなユースケースでデータを表示するのに十分な柔軟性を備えていることです。
ページは昨日です:長生きする構造化されたコンテンツ!
このアプリを作成すると、データをページ形式にするという原則は過去のものであることがもう一度わかりました。 データがどこに行くのかわかりません—無制限の数のユースケースに備える必要があります。
振り返ってみると、私が達成したことは次のとおりです。
- クラウドにコンテンツ管理システムを導入する
- データベースのメンテナンスに対処する必要はありません
- 完全なJavaScriptテクノロジースタック
- 効率的な静的ウェブサイトを持つ
- いつでもどこでも私のコンテンツにアクセスするためのAndroidアプリを持っている
そして最も重要な部分:
コンテンツを構造化してアクセスできるようにすることで、日常生活を改善することができました。
このユースケースは今のところ些細なことのように見えるかもしれませんが、毎日作成する製品について考えると、さまざまなプラットフォームでのコンテンツのユースケースは常に増えています。 今日、私たちはモバイルデバイスがついに古い学校のデスクトップ環境を追い越していることを受け入れますが、車、時計、さらには冷蔵庫などのプラットフォームはすでにスポットライトを待っています。 今後のユースケースすら考えられません。
それでは、準備を整えて構造化コンテンツを中央に配置してみましょう。最終的にはデータベーススキーマではなく、将来に向けて構築することです。
SmashingMagの詳細:
- Node.jsを使用したWebスクレイピング
- Sails.jsを使用したセーリング:Node.js用のMVCスタイルのフレームワーク
- あなたのデザインを整えるための40の旅行アイコン
- Webpackの詳細な紹介