【ビギナー向け】Jamstackサイトの簡単な作り方
2021.12.27
この記事は約3分で読めます
目次
この記事の筆者:三好アキ
🔹 専門用語なしでプログラミングを教えるメソッドに定評があり、1200人以上のビギナーを、最新のフロントエンド開発入門に成功させる。
🔹 Amazonベストセラー1位を複数回獲得している『はじめてつくるReactアプリ with TypeScript』著者。
Amazon著者ページはこちら → amazon.co.jp/stores/author/B099Z51QF2
React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。登録はこちらから → 無料メルマガ登録
JamstackとヘッドレスCMS
本記事はまず最初にGatsbyにマークダウンのブログ機能を実装し、その後、ヘッドレスCMSのContentfulをつなげる方法を紹介します。
ヘッドレスCMSを活用することで、プログラマーやエンジニアではない人でも、コードをさわることなく簡単にブログ記事の作成や編集ができるようになるので、Jamstackサイトのコンテンツ管理(CMS)には多くの場合ヘッドレスCMSが接続されています。
本記事ではホスティングにNetlifyを使います。
記事の後半ではGitHubが必要なので、プッシュができるように設定は済ませておいてください。
流れは次のようになります。
ステップ1:Gatsbyにマークダウンのブログ機能を実装
ステップ2:Contentfulにブログ記事データを保管
ステップ3:ContentfulとGatsbyをつなぐ
ステップ4:ContentfulとNetlifyをつなぐ
ロジック部分の説明だけなので、スタイルは各自お好みで適用してください。
この記事を読んでいる人は、もうすでにJamstackのことを知っていると思いますが、くわしく知りたい方は次の記事を参考にしてください。
ヘッドレスCMSについては次の記事に詳しく書いてあります。
Gatsbyにマークダウンのブログ機能を実装する
まずGatsbyのスターターキットに、マークダウンのブログ機能を追加しましょう。
最初にスターターサイトをダウンロードするので、ターミナルで以下のコマンドを実行します。
npx gatsby new gatsby-starter-default https://github.com/gatsbyjs/gatsby-starter-default
次にブログ記事を一覧表示するblog.js
、個別記事を表示するsingleBlog.js
を作ります。
まずはblog.js
から始めます。
├──pages
│ ├── 404.js
│ ├── blog.js ←追加
│ ├── index.js
│ ├── pages-2.js
│ ├── using-ssr.js
│ └── using-typescript.tsx
次のコードを書きます。
// blog.js
import * as React from "react"
import { graphql, Link } from "gatsby"
const Blog = (props) => {
return (
<div>
<h1>ブログページ</h1>
{props.data.allMarkdownRemark.edges.map((singleBlog, index) =>
<div key={index}>
<h2><Link to={`/blog${singleBlog.node.fields.slug}`}>{singleBlog.node.frontmatter.title}</Link></h2>
<p>{singleBlog.node.frontmatter.date}</p>
</div>
)}
</div>
)
}
export default Blog
export const query = graphql`
query BlogQuery {
allMarkdownRemark(
sort: {fields: frontmatter___id, order: DESC}
) {
edges {
node {
fields {
slug
}
frontmatter {
date
summary
id
title
}
}
}
}
}
`
singleBlog.js
をtemplates
フォルダ内に作ります。
├──templates
│ ├── singleBlog.js ←追加
│ └── using-dsg.js
次のコードを書きます。
// singleBlog.js
import * as React from "react"
import { graphql } from "gatsby"
const SingleBlog = (props) => {
return (
<div>
<h1>{props.data.markdownRemark.frontmatter.title}</h1>
<p>{props.data.markdownRemark.frontmatter.date}</p>
<div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} />
</div>
)
}
export default SingleBlog
export const query = graphql`
query SingleBlogQuery($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
frontmatter {
date
title
summary
id
}
html
}
}
`
次に、ブログ記事のマークダウンファイルを収納するフォルダをsrc
内に作ります。
├──src
│ ├── components
│ ├── data ←追加
│ ├── images
│ ├── pages
│ └── templates
data
内には6つのマークダウンファイルを作ります。
├── data
│ ├── first.md
│ ├── second.md
│ ├── third.md
│ ├── fourth.md
│ ├── fifth.md
│ └── sixth.md
それぞれのマークダウンファイルには、下のように基本的な情報を書きます。
// first.md
---
id: "1"
title: "記事1"
date: "2021-06-21"
summary: "記事1の要約"
---
1つ目の記事。
// second.md
---
id: "2"
title: "記事2"
date: "2021-06-22"
summary: "記事2の要約"
---
2つ目の記事。
// third.md
---
id: "3"
title: "記事3"
date: "2021-06-23"
summary: "記事3の要約"
---
3つ目の記事。
// fourth.md
---
id: "4"
title: "記事4"
date: "2021-06-24"
summary: "記事4の要約"
---
4つ目の記事。
// fifth.md
---
id: "5"
title: "記事5"
date: "2021-06-25"
summary: "記事5の要約"
---
5つ目の記事。
// sixth.md
---
id: "6"
title: "記事6"
date: "2021-06-26"
summary: "記事6の要約"
---
6つ目の記事。
これでブログのデータと、それを表示する部分ができました。
次は表示するための仕組みづくりです。
ブログ機能に必要なプラグインをインストールしましょう。
npm install gatsby-transformer-remark
これと、デフォルトですでに入っているgatsby-source-filesystem
をgatsby-config.js
に追加します。
// gatsby-config.js
module.exports = {
siteMetadata: {
...
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
// ⬇ 追加
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/src/data`,
},
},
`gatsby-transformer-remark`,
// ⬆ 追加
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
...
gatsby-node.js
に、上で作ったコンポーネントにマークダウンファイルを流し込むためのコードを追加します。
// gatsby-node.js
// ⬇ 削除
exports.createPages = async ({ actions }) => {
const { createPage } = actions
createPage({
path: "/using-dsg",
component: require.resolve("./src/templates/using-dsg.js"),
context: {},
defer: true,
})
}
// ⬆ 削除
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: `blog${node.fields.slug}`,
component: path.resolve(`./src/templates/singleBlog.js`),
context: {
slug: node.fields.slug,
},
})
})
}
ここまで加えた変更をすべて保存します。
gatsby develop
でGatsbyを起動し、http://localhost:8000/blog
をブラウザで開くと、ブログ機能が追加されているのがわかります。
次は、ここまでマークダウンファイル上で行っていたブログ記事の作成や編集の作業を、ヘッドレスCMSのContentful上で行えるようにします。
Contentfulにブログ記事データを保管する
次のURLを開き、アカウントを作成します。
Contentfulにはデータを保存する大枠としての「Space」があり、その中に個別のデータを保存する領域を作っていきます(下図参照。図中の「textBody」はブログの本文データ、「slug」はブログ記事のURLを指します)。
まずは「blog」という領域を作ります。
ログイン後のContntfulの上部メニューバーで「Content model」をクリックして、右側の「Add conent type」を押し、「Name」と「Api Identifier」には「blog」「Description」には「ブログ」と入力しましょう。
そうすると、この「blog」という領域に保存するデータの項目を決めるよう促されるので、「Add a field」ボタンを押します。
「field」はマークダウンファイルのfrontmatter
部分の項目に該当するので、それぞれ作ります。
(「Name」と「Field ID」には、例えばid
であれば「id」、title
であれば「title」というように、同じ文字を入力します。)
id
→「Number」
date
→「Date and time」
title
→「Text」
summary
→「Text」
ブログの本文にあたるtextBody
、そしてslug
も作ります。
textBody
→「Text」を選択後、「Long text, full-text search」
slug
→「Text」
これで下図のようにブログのデータとして保存する各項目ができました。右上にある「Save」ボタンを押して保存します。
ブログデータを保存する領域ができたので、次はデータを入力していきます。
画面上部メニューの「Content」をクリックし、中心部に表示される「Add blog」ボタンを押してください。
マークダウンファイル6つのデータを、それぞれここにコピー&ペーストします。
「slug」欄は次のように入力しましょう。
1つ目の記事: /contentful-first
2つ目の記事: /contentful-second
3つ目の記事: /contentful-third
4つ目の記事: /contentful-fourth
5つ目の記事: /contentful-fifth
6つ目の記事: /contentful-sixth
6つの記事をすべて入力すると次のようになります。
これでContentful側の準備が完了したので、このデータをGatsbyサイトからGraphQLで呼び出して表示させます。
ContentfulとGatsbyをつなげる
Contentfulからデータを取得するためのプラグインをインストールします。
npm install gatsby-source-contentful
これをgatsby-config.js
に追加します。
// gatsby-config.js(#5-j1)
...
plugins: [
`gatsby-plugin-sass`,
`gatsby-transformer-remark`,
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
// ⬇追加
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: ``,
accessToken: ``,
},
},
// ⬆追加
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/src/data`,
},
},
...
現在は空欄となっているspaceId
とaccessToken
を入力していきます。
Contentfulの上部メニューバーの「Settings」から、「API Keys」を選びます。
「Content delivery / preview tokens」タブが開かれていることを確認し、右側の「Add API key」ボタンをクリックします。
「name」は「firstjamstack」と打ち、右側の「Save」ボタンで保存しましょう。
「Space ID」をspaceId
に、「Content Delivery API - access token」をaccessToken
に、それぞれコピーして下のように貼り付けます。
// gatsby-config.js
...
plugins: [
`gatsby-plugin-sass`,
`gatsby-transformer-remark`,
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `xxxxxx6qqsf7`, // 追加
accessToken: `xxxxx_4sEXPxxxxxxh51DLxxxxxxoE`, // 追加
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/src/data`,
},
},
...
これでGatsbyとContentfulを接続できたので、gatsby develop
でGatsbyを起動させ、http://localhost:8000/___graphql
をブラウザで開きます。
すでに起動している場合は再起動してください。
まずはblog.js
に必要なデータを取得しましょう。
一番左の画面でallContentfulBlog
→ edges
→ node
と進みます。
blog.js
で必要なのはdate
、summary
、id
、title
、slug
なので、チェックを入れます。
中央画面のGraphQLクエリをコピーして、現在のものと置き換えます。
ここにはブログ記事の順番を日付順に整えるコード、(sort: {fields: date, order: DESC})
も追加してあります。
// blog.js
export default Blog
export const query = graphql`
// ⬇貼り付け
query BlogQuery {
allContentfulBlog(sort: {fields: date, order: DESC}) {
edges {
node {
date
summary
slug
title
id
}
}
}
}
// ⬆貼り付け
`
マークダウンファイルから取得したデータの構造と、Contentfulから取得したデータの構造は異なるので、次のように修正します。
// blog.js
import * as React from "react"
import { graphql, Link } from "gatsby"
const Blog = (props) => {
return (
<div>
<h1>ブログページ</h1>
{props.data.allContentfulBlog.edges.map((singleBlog, index) =>
<div key={index}>
<h2><Link to={`/blog${singleBlog.node.slug}`}>{singleBlog.node.title}</Link></h2>
<p>{singleBlog.node.date}</p>
</div>
)}
</div>
)
}
export default Blog
...
保存します。
次はsingleBlog.js
の作業です。
gatsby-node.js
を開き、全体を次のように修正します。
// gatsby-node.js
const { createFilePath } = require(`gatsby-source-filesystem`)
const path = require(`path`)
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allContentfulBlog {
edges {
node {
slug
}
}
}
}
`)
result.data.allContentfulBlog.edges.forEach(({ node }) => {
createPage({
path: `blog${node.slug}`,
component: path.resolve(`./src/templates/singleBlog.js`),
context: {
slug: node.slug,
},
})
})
}
保存します。
次はsingleBlog.js
からContentfulのデータを取得し、表示させます。
gatsby-node.js
を編集したのでGatsbyを再起動させますが、今の状態ではsingleBlog.js
でエラーが出るので、再起動の前に下のようにコメントアウトしておきましょう。
// singleBlog.js
import * as React from "react"
import { graphql } from "gatsby"
const SingleBlog = (props) => {
return (
<div>
{/* <h1>{props.data.markdownRemark.frontmatter.title}</h1>
<p>{props.data.markdownRemark.frontmatter.date}</p>
<div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} /> */}
</div>
)
}
export default SingleBlog
/* export const query = graphql`
query SingleBlogQuery($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
frontmatter {
date
title
summary
id
}
html
}
}
` */
gatsby develop
を実行し、http://localhost:8000/___graphql
を開きましょう。
左の画面でcontentfulBlog
にチェックを入れ、singleBlog.js
で必要なdate
、title
、そして本文にあたるtextBody
を選択します。textBody
はchildMarkdownRemark
のhtml
を選択します。
中央画面のクエリをコピーし、singleBlog.js
ファイル下部でコメントアウトしているクエリは消して、貼り付けます。
なお、ここでは変数「slug」も渡す必要があるので、書き加えておきます。
// singleBlog.js
...
export default SingleBlog
export const query = graphql`
query SingleBlogQuery ($slug: String!){
contentfulBlog (slug: { eq: $slug }){
date
title
textBody {
childMarkdownRemark {
html
}
}
}
}
`
そしてblog.js
の時と同じように、コードを修正します。
// single-blog.js
import * as React from "react"
import { graphql } from "gatsby"
const SingleBlog = (props) => {
return (
<div>
<h1>{props.data.contentfulBlog.title}</h1>
<p>{props.data.contentfulBlog.date}</p>
<div dangerouslySetInnerHTML={{ __html: props.data.contentfulBlog.textBody.childMarkdownRemark.html }} />
</div>
)
}
export default SingleBlog
...
これでContentfulからデータを取得できるようになりました。
ContentfulとNetlifyをつなげる
自分のGitHub(GitLabなども可)に、ここまで開発したコードをプッシュします。
Netlify(https://www.netlify.com)にGitHubアカウントを使ってログインし、メニュー画面上部「Sites」をクリックして、画面左に「Add new site」ボタンがあるので、そこから「Import an existing project」を選びます。
「GitHub」を選択し、指示に従って、先ほどコードをプッシュしたリポジトリを選びます。
下部の「Deploy site」ボタンを押すと、Netlify上でBuildが行われ、自動でサイトが公開されます。
次はNetlifyとContentfulを、WebHookを使ってつなげます。
WebHookによって、Contentful内のデータの更新が行われるたびにContentfulからNetlifyに「Buildを行ってください」という指示を出すのです。
Netlifyの「Site settings」をクリックし、画面左のメニュー画面から「Build & deploy」を選び、少し下にスクロールすると「Build hooks」というWebHookを設定するところがあります。
「Add build hook」ボタンを押し、「Build hook name」には好きな名前を入力したら「Save」を押します。
生成されたURLをコピーします。
Contentfulへ戻り、メニュー画面上部の「Settings」から「Webhooks」を選びます。
画面右に「Netlify」とあるので、「Add」をクリックします。
「Netlify build hook URL」に先ほどコピーしたURLを貼り付け、「Create webhook」ボタンを押します。
これでNetlifyとContentfulの接続が完了しました。
Contentfulの記事データに変更を加えて「Publish」ボタンを押すと、Netlify上で自動でBuildが始まり、サイトに変更が反映されるのがわかります。
以上でヘッドレスCMSを接続したJamstackサイトが完成です。
本記事では細かい説明や詳しい図解を省略して足早に解説しましたが、拙著「はじめてつくるJamstackサイト with ヘッドレスCMS」では、ビギナーの引っかかりやすいところをわかりやすく解説しているので、参考にしてください。
メルマガ配信中
(from 三好アキ/エンジニア)
React、Next.js、TypeScriptなど最新のウェブ開発のお役立ち情報を、ビギナー向けにかみ砕いて無料配信中。
(*配信はいつでも停止できます)