Jamstack は Web 開発のアーキテクチャです。この記事では Jamstack についてご紹介し、Jamstack 構成のシンプルなブログサイトを作ります。

Jamstack とは

Jamstack は Netlify の 創業者 Matt Biilmann 氏が名付けたアーキテクチャです。Jamstack とは一体なんでしょうか。Jamstack の公式サイト jamstack.org では、Jamstack を次のように定義しています。

A modern architecture —

Create fast and secure sites and dynamic apps with JavaScript, APIs, and prerendered Markup, served without web servers.

https://jamstack.org/

和訳すると「JavaScript、API、プリレンダリングされた Markup を使用し、Web サーバなしで提供される高速で安全なサイトおよび動的アプリケーションを作成するモダンアーキテクチャ」となります。

Jamstack は「JavaScript」「API」「プリレンダリングされた Markup」の3つの要素を持ちます。「Jam」の「J」が「JavaScript」、「a」が「API」、そして「m」が「Markup」です。また、Jamstack は Web サーバに依存しないという特徴があります。

JavaScript

すべての動的プログラミングはクライアント(ブラウザ)の JavaScript が行います。クライアントの JavaScript は特定の方法を指しません。フレームワークやライブラリを使ってもよいですし、Vanilla JavaScript でもかまいません。

API

すべてのサーバの処理は Web API を通じて行います。クライアントの JavaScript が Web API を呼び出し、データの送受信を行います。サードパーティのサービスを利用したい場合は Web API を通じて利用します。

プリレンダリングされた Markup

プリレンダリングとは、サイトやアプリケーションの各ページの初期状態を HTML ファイルとして事前に生成することを指します。ページの見た目を定義するテンプレートに実際のデータを注入し、その結果を HTML ファイルとして生成します。プリレンダリングは静的サイトジェネレータやビルドツールを用いてビルド時に行います。

なお、2020年1月時点の公式サイトでは Jamstack の定義から「JavaScript」「API」「Markup」の文字が消えています。しかし、「Jam」の説明のため、この記事では 2019年12月時点の定義をご紹介しました。2020年1月時点の公式サイトの Jamstack の定義は次の通りです。

Fast and secure sites and apps delivered by pre-rendering files and serving them directly from a CDN, removing the requirement to manage or run web servers.

https://jamstack.org/

和訳すると「ファイルをプリレンダリングし CDN から配信される、高速で安全なサイトとアプリケーションで、Webサーバの管理・実行の必要がない」となります。

また、2019年11月時点の公式サイトでは Jamstack の表記が「JAMstack」となっていましたが、2019年12月半ばに「Jamstack」に変更されています。この記事では引き続き、変更後の「Jamstack」と表記します。

ブログサイトの作成

それでは Jamstack 構成のブログサイトを作ってみましょう。

今回は、データソースとして Markdown ファイルを用意します。次に Gatsby を使って Markdown ファイルの内容をデータとして取得し、ブログサイトを作ります。静的サイトジェネレータの Gatsby を使うことで、ビルド時に静的ファイルを出力します。最後に Netlify を使ってブログサイトのビルド、デプロイを行います。

  • データソース:Markdown ファイル
  • 静的サイトジェネレータ:Gatsby
  • ビルド、デプロイ:Netlify

※Gatsby: React ベースの静的サイトジェネレータ
※Netlify: Web サイトの自動化のためのオールインワンのプラットフォーム。ビルド、デプロイ、ホスティングといった機能を提供している

また、サンプルコードは GitHub にアップしています。合わせてご参照ください。
https://github.com/mokajima/jamstack-demo

※後述の Netlify にデプロイするにて、デプロイ時に Git のリポジトリを使用します。ご自身のリポジトリをご用意いただくか、サンプルコードをフォークいただき、お試しください。

環境構築

Gatsby の CLI を使って環境構築を行います。以下のコマンドを実行してください。

$ npx gatsby new jamstack-demo https://github.com/gatsbyjs/gatsby-starter-default

このコマンドは gatsby-starter-default というスターターコードを使い、jamstack-demo というディレクトリ名で Gatsby のプロジェクトを作成します。

プロジェクトの作成が終わったら cd jamstack-demojamstack-demo ディレクトリに移動し、yarn start で開発サーバを起動します。開発サーバを起動すると http://localhost:8000 でサイトの表示を確認できるようになります。

$ cd jamstack-demo; yarn start

データソースの追加

データソースの Markdown ファイルを追加しましょう。src ディレクトリ直下に markdown-pages ディレクトリを作り、post1.md post2.md post3.md を追加します。

--- のブロック内は frontmatter と呼ばれるデータです。「キー: 値」の形式で記述し、Markdown ファイルにデータを持たせることができます。ここでは title date slug を指定しています。

なお、Gatsby では様々なデータソースを扱うことができ、Markdown ファイルの他にも、API、データベース、CMS、ローカルファイルなどを利用可能です。

ソースプラグインの追加

Gatsby で各種データソースを扱うには「ソースプラグイン」と呼ばれる専用のプラグインを追加する必要があります。ソースプラグインはデータソースからデータを取得するために使用します。Markdown ファイルをデータソースとして扱うには gatsby-source-filesystem が必要です。このプラグインは gatsby-starter-default ではインストール済みですが、設定の追加が必要です。そこで gatsby-config.js を編集します。gatsby-config.js はその名の通り、Gatsby の設定に関する記述を行うためのファイルです。

gatsby-config.jsplugins 配列の中に resolve: gatsby-source-filesystem の箇所があります。その下の options を書き換えてください。

resolve には使用するプラグイン名を指定します。使用するプラグインによってはオプションを持たせることができ、options を使って指定します。ここでは name に識別用の名前、path にデータソースの場所を指定しています。

gatsby-config.js を書き換えたら開発サーバを再起動してください。

開発サーバを再起動したら、Markdown ファイルをデータとして取得できているか確認してみましょう。Gatsby では GraphQL を使ってデータにアクセスします。

GraphQL

GraphQL は Facebook が開発した API のためのクエリ言語です。GraphQL のクエリは query クエリ名 {} の形で定義します。query キーワードとクエリ名は省略可能です。

以下のクエリは、データソースの追加で追加した Markdown ファイルをデータとして取得できているかを確認するためのクエリです。ファイルの相対パスの relativePath とファイルの識別名(データソースの追加gatsby-config.jsgatsby-source-filesystem の設定で指定した name)の2つのフィールドを取得しています。

query MyQuery {
  allFile {
    edges {
      node {
        relativePath,
        sourceInstanceName
      }
    }
  }
}

query は「データを取得する」という操作を表します。query では取得したいデータの構造を定義すると、その通りのレスポンスを得ることができます。例えば、上記のクエリを実行した結果は次のようになります。

※GraphQL では query の他にも mutationssubscription という操作が存在します。

allFile edges node relativePath sourceInstanceName はデータのフィールドです。allFile はソースプラグイン gatsby-source-filesystem によって利用可能となったフィールドで、すべてのファイルを取得します。使用するソースプラグインによって、利用可能なフィールドが変わります。

GraphiQL

GraphiQL は GraphQL の IDE(統合開発環境)です。GraphQL のクエリを実行したり、各フィールドの構造を確認したりすることができます。開発サーバの起動中は http://localhost:8000/___graphql で利用可能です。

以下のクエリを GraphiQL 上で実行してみましょう。GraphiQL を開くと左側にエクスプローラがあり、その右隣にはクエリを入力するエディタがあります。エディタに以下のクエリを入力し、再生ボタンをクリック、ないしは Ctrl-Enter を押すとクエリが実行されます。クエリ結果はエディタの右隣に表示されます。

query MyQuery {
  allFile {
    edges {
      node {
        relativePath,
        sourceInstanceName
      }
    }
  }
}

クエリの実行結果から、データソースの追加で追加した Markdown ファイルをデータとして取得できていることがわかります。

なお、GraphiQL のエクスプローラでは各フィールドの構造を確認したり、クエリを組み立てたりすることが可能です。例えば、allFile の左横の三角形を開いていくと、edgesnode が、noderelativePathsourceInstanceName が含まれているのがわかります。また、各フィールド名の左横のチェックボックスにチェックを入れると、そのフィールドがエディタのクエリに追加されます。

トランスフォーマープラグインの追加

ソースプラグインの追加で Markdown ファイルをデータとして取得できるようになりました。次は Markdown ファイルをパースし、HTML データとして利用可能にする必要があります。そこで、トランスフォーマープラグインの gatsby-transformer-remark を追加します。トランスフォーマープラグインは「トランスフォーマー(transformer)」とあるように、あるデータを別のデータに変換するためのプラグインです。gatsby-transformer-remark は Markdown ファイルをパースし、HTML データに変換します。

$ yarn add gatsby-transformer-remark

gatsby-transformer-remark をインストールしたら、gatsby-config.jsgatsby-transformer-remark を追記します。

追記したら、開発サーバを再起動してください。

gatsby-transformer-remark によって、allMarkdownRemarkMarkdownRemark といったフィールドがクエリで利用可能になります。GraphiQL で以下のクエリを実行すると、Markdown ファイル内で指定した title slug date に加えて、本文の HTML データを取得することができます。

query MyQuery {
  allMarkdownRemark {
    edges {
      node {
        frontmatter {
          title
          date
          slug
        }
        html
      }
    }
  }
}

このデータを使ってページの作成を行っていきましょう。

一覧ページの作成

Gatsby では、src/pages に追加したコンポーネントファイルがそのままルーティングとなります。そのため、src/pages/hoge.js があれば、/hoge というページが生成されます。

今回はブログサイトを作るため、トップページを一覧ページとしましょう。src/pages/index.js がトップページに該当します。index.js を以下のように変更してください。

export const query = graphql ではページのクエリを定義しています。allMarkdownRemark はすべての Markdown ファイルを取得するためのフィールドです。ここでは allMarkdownRemark の構造に従い、4つのフィールド title date slug excerpt の値を取得するクエリを定義しています(allMarkdownRemark の構造は GraphiQL のエクスプローラで確認可能です)。

また、フィールドによっては引数を渡すことができます。ここでは dateformatString を引数として渡し、日付のフォーマット方法を指定しています。

そして、export const 任意の変数名 = graphql`` の形でクエリを囲うことで、Gatsby がページのクエリとして認識し、その実行結果をそのページのコンポーネント(ここでは IndexPage)に渡します。

IndexPage コンポーネントでは data を props として受け取り、それを元に一覧ページを作成しています。

これで一覧ページが作成できました。

一覧ページの並び替え

一覧ページを見てみると、並び順が日付順になっていないようです。一覧のソートを行うため、date と同じように、allMarkdownRemark に引数を指定します。

allMarkdownRemark では sort を引数として指定可能です。引数の情報は GraphiQL で確認することができます。GraphiQL のエディタ上のフィールド名を Ctrl を押した状態でクリックします。すると、クエリの実行結果の右隣に allMarkdownRemark の情報が表示され、ARGUMENTS(引数) に filter limit skip sort があるのがわかります。

ARGUMENTS の sort: MarkdownRemarkSortInputMarkdownRemarkSortInputsort の型を表しています。MarkdownRemarkSortInput をクリックすると、MarkdownRemarkSortInput の情報が表示され、fieldsorder を持つことがわかります。

また、fields: [MarkdownRemarkFieldsEnum]MarkdownRemarkFieldsEnum をクリックすると、fields に指定可能な値がわかります。このサンプルコードでは id frontmatter___title frontmatter___date frontmatter___slug を指定可能です。

ここでは fields[frontmatter___date] を、orderDESC を指定し、date の降順で表示するようにしました。

詳細ページの作成

次は詳細ページを作っていきましょう。一覧ページの作成では、トップページを一覧ページとするため、src/pages/index.js を編集しました。詳細ページの場合は記事によって URL が変わるため、src/pages/ 内の単一ファイルでページを作成することができません。詳細ページを動的に生成するためには、ページの雛形となるテンプレートを作成し、gatsby-node.js にページの生成ロジックを記述します。

テンプレートの作成

まず、データを注入する元となるテンプレートを作成します。src 上で templates ディレクトリを作成し、post.js を追加します。

src/pages/index.js では allMarkdownRemark を使いましたが、今回は markdownRemark を使っています。allMarkdownRemark がすべての Markdown ファイルを取得するためのフィールドであるのに対し、markdownRemark は単独の Markdown ファイルを取得するためのフィールドです。

src/pages/index.jsallMarkdownRemark では sort を引数として渡しましたが、markdownRemark では frontmatter を引数として渡しています。frontmatter では取得する記事の条件を指定でき、ここでは frontmatterslug$slug と等しい記事を指定しています。そして、2つのフィールド htmltitle の値を取得しています。

markdownRemark の引数の情報は一覧ページの並び替えと同様に GraphiQL で確認可能です。
$slugquery($slug: String!) については後述します。

ページの生成ロジックを作成

ページの動的生成を行うには Gatsby Node API を使用する必要があります。Gatsby Node API が利用可能となるのが gatsby-node.js というファイル内です。そこで、gatsby-node.js にページの生成ロジックを記述します。

gatsby-node.js を以下のように書き換えてください。

gatsby-node.js では exports.API名 = gatsbyNodeHelpers => {} の形で API を使用します。API は第一引数に gatsbyNodeHelpers を受け取ります。gatsbyNodeHelpers はヘルパーオブジェクトで、Gatsby Node API で共通です(一部 API 特有のフィールドもあります)。ここでは gatsbyNodeHelpersgraphqlactions を使用しています。

actions は関数の集まりで、その中の createPage を用いてページを動的に生成することができます。なお、Gatsby では状態管理のために内部で Redux を使っており、actions の持つ各関数は Redux の bindActionCreators でバインドされた action creators と同等です。

const result = await graphql( では allMarkdownRemark を使い、各記事の slug を取得するクエリを定義しています。取得結果をループで回しているのが result.data.allMarkdownRemark... の箇所です。ループごとに createPage を呼び出し、ページを生成します。

createPage には path component context を持つオブジェクトを渡しています。path はその記事のパス、componentテンプレートの作成で作成したテンプレートを指定しています。context に渡すデータはページクエリ内で GraphQL の変数として使用することができます。ここでは slug を渡しており、それが src/templates/post.jsquery($slug: String!) { の部分です。String! の部分は変数 $slug の型が文字列型で null を許容しないことを意味します。

gatsby-node.js を書き換えたら開発サーバを再起動してください。/post1/post2 にアクセスすると、詳細ページが表示されます。

ブログサイトをプリレンダリングする

一通りブログサイトの実装が終わりました。Jamstack 構成ではプリレンダリングした静的ファイルをデプロイするため、試しに静的ファイルを出力してみましょう。yarn build を行うと、public ディレクトリに静的ファイルが出力されます。

$ yarn build

yarn serve を実行すると、出力した静的ファイルをローカルで実行することができます。

$ yarn serve

Netlify にデプロイする

作成したブログサイトを Netlify にデプロイします。Netlify のアカウントを作成するか、ログインを行ってください。Netlify のデプロイはサインアップ・ログイン後の画面の「New site from Git」から行います。

「New site from Git」をクリックしたら、リポジトリの選択を行います。リポジトリがホスティングされているサービスを「GitHub」「GitLab」「Bitbucket」のいずれかから選択します。サンプルコードは GitHub のため、GitHub を選択します。

リポジトリの一覧が表示されるため、デプロイを行うリポジトリを選択してください。

リポジトリを選択したら、デプロイの設定を行います。「Branch to deploy」ではデプロイしたいブランチ名を選択します。master 以外を指定することも可能です。「Basic build settings」は初期設定のままで問題ありません。設定を確認したら、「Deploy site」をクリックします。

「Deploy site」をクリックすると、デプロイしたサイトの詳細ページに遷移します。黄色の文字で「Site deploy in progress」と表示されている場合はデプロイ中です。デプロイが終わると緑色の文字でサイトの URL が表示されます。サイトの URL は自動的に割り当てられます。

サイトの詳細ページでは「Site settings」の「Change site name」からサイトの URL を変更したり、「Domain settings」からカスタムドメインの設定を行ったりなど、様々な操作、設定を行うことが可能です。ここではサイトの URL を mokajima-jamstack-demo.netlify.com に変更してみました。

また、デプロイの設定の「Branch to deploy」ではデプロイしたいブランチ名を選択しましたが、このブランチに新しいコミットをプッシュすると、Netlify がそれを検知して自動でビルド、デプロイを行ってくれます。デプロイの履歴は管理され、クリックひとつで前のデプロイの状態に戻す、といったことも可能です。

なお、ブログサイトをプリレンダリングするで出力した public ディレクトリを「Want to deploy a new site without connecting to Git?」と書かれたエリアにドラッグ&ドロップし、デプロイすることも可能ですが、この場合は Netlify の自動ビルド、デプロイが行われません。

これでデプロイは完了です。

最後に

今回はデータソースとして Markdown ファイルを扱いましたが、Gatsby では WordPress の REST API や Contentful、microCMS のような Headless CMS など、様々なデータソースを扱うことができます。また、Jamstack は Gatsby に限定されないため、要件に応じて任意の静的サイトジェネレータを使うことが可能です。

この記事ではご紹介しませんでしたが、外部 API を利用することで Jamstack 構成のサイトに検索機能や決済機能などの機能を盛り込む、といったことも可能です。

フロントエンドの技術で行える Jamstack 構成の開発をぜひ試してみてください。

参考

Jamstack

Gatsby

Netlify