published on : 2019.10.15Web制作

#1 Gatsby + WordPress REST APIでブログを作ろう【導入+基本構成編】

どうも、サウナ大好きケンタロウ(@kentaro_koga)です

この記事では「GatsbyとWordPress REST APIを使ってのブログの作り方」を解説していきます。

Gatsbyてなに?髪の毛に塗るの?

Gatsbyは静的サイトジェネレーターでreactベースで作られています。

graphqlが簡単に使えるのでWordPressやContentfulなどのCMSからデータを引っ張ってこれます。

最終的にGatsbyが静的ページ(HTML / CSS / JS)を生成してくれるので、それを好きなサーバーに置くだけで完成です

すでに生成された静的ページなので読み込みが早く、SPAで、Preloadingもしてくれるのでページ遷移も早いです。

色々特徴はありますが、今回はブログの作り方の紹介なので割愛します。

ではこれからステップバイステップで解説していきます。

    前提
  • Mac
  • gatsby-starter-defaultを使用しての解説
  • nodeインストール済み(記事執筆時の僕の環境はv10.15.3です)
  • Gatsby version: 2.15.14

では参ります

gatsbyの準備

gatsbyがまだインストールされていない場合は以下のコードをターミナルで打ちます

npm install -g gatsby-cli

ちゃんとインストールされているかは以下のコードで確認できます

gatsby --version

これでgatsbyコマンドが使えるようになりました

次にサイトを作りたいディレクトリを作って、そのディレクトリまでcdコマンドで移動してgatsbyコマンドでサイトを作ります

gatsby new プロジェクト名

gatsby new プロジェクト名 スターターのgithubのurlでお好みのスターターを使えます

スターターとは最初に生成されるテンプレみたいなもので、何も指定しないとgatsby-starter-defaultになります

cdで作成されたプロジェクトのディレクトリに移動します
cd プロジェクト名

gatsby developをすると開発環境が構築されます
gatsby develop

local:8000に行くとこんな感じに表示されていると思います

これでgatsbyの準備は完了です

gatsbyのプラグインを追加

WordPress REST APIからデータを引っ張ってくるにはgatsbyが提供するプラグイン「gatsby-source-wordpress」を入れます。

npm install gatsby-source-wordpress

package.jsonに「gatsby-source-wordpress」が追加されているかと思います。

gatsby-config.jsにも追加します

gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    {
      resolve: "gatsby-source-wordpress",
      options: {
        baseUrl: “WordPressがインストールされている場所”,
        protocol: "https",
        hostingWPCOM: false,
        useACF: false,
        includedRoutes: [
          "**/categories",
          "**/posts",
          "**/pages",
          "**/media",
          "**/tags",
          "**/taxonomies",
          "**/users",
        ],
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}

“gatsby-source-wordpress”のところしか追加してませんので、とりあえず他はノータッチで大丈夫です

    やったこと
  • baseUrlにWordPressをインストールしている場所を指定します
  • WordPress.comを使用する場合はhostingWPCOMをtrueにします
  • Advanced Custom Fieldsを使いたい時はuseACFをtrueに変更します

とりあえずはWordPressから記事を引っ張ってこれるようにします

*今回は自分が持っている仮ドメインにWordPressをインストールしています

Local by Flywheelを使ってWordPressの環境を作って開発の練習をしてもいいかと思います

http://localhost:8000/___graphqlでちゃんとデータが取れているか確認できます。

    POINT
  • allWordpressPost:全投稿記事
  • allWordpressPage:全固定ページ

再生ボタンを押すと取得できたデータが表示されます

記事ページを作る

記事ページのtemplateを作ります。

srcディレクトリの中にtemplateディレクトリを作ります。

templateディレクトリの中にBlogPost.jsを作ります。

BlogPost.js
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"
const BlogPostTemplate = ({ data }) => (
  <Layout>
    <SEO
      title={data.wordpressPost.title}
      description={data.wordpressPost.excerpt}
    />
    <h1>{data.wordpressPost.title}</h1>
    <span>{data.wordpressPost.date}</span>
    <Img fluid={data.wordpressPost.featured_media.localFile.childImageSharp.fluid} />
    <div
      dangerouslySetInnerHTML={{ __html: data.wordpressPost.content }}
    />
  </Layout>
)
export default BlogPostTemplate
export const query = graphql`
  query($id: Int!) {
    wordpressPost(wordpress_id: { eq: $id }) {
      title
      content
      excerpt
      date(formatString: "MMMM DD, YYYY")
      featured_media {
        localFile {
          childImageSharp {
            fluid(maxWidth: 1000) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`

graphqlでのデータの取り方はhttp://localhost:8000/___graphqlを見るとだいたいわかります。

このテンプレートにgraphqlでWordPressから引っ張ってきたデータが入り、記事のページが生成されていきます。

*gatsby-imageというプラグインを使用してfeatured_mediaから画像を引っ張ってきています
詳しくは(https://www.gatsbyjs.org/tutorial/wordpress-image-tutorial/)に記載してありますが、英語なので今度基本的な部分を紹介しようかと思います

「gatsby-transformer-sharp」「gatsby-plugin-sharp」「gatsby-image」というプラグインが必要になりますが、今回使用しているデフォルトのスターターに最初から入っているので、このままで動きます(gatsby-config.jsで入っているので確認できます)

gatsby-node.jsでページを生成する設定をする

pathパッケージを使うとより簡単にページの生成が行えるのでインストールします

npm install path

gatsby-node.jsの設定をします。

gatsby-node.js
const path = require(`path`)
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
  const result = await graphql(`
    {
      allWordpressPost {
        edges {
          node {
            slug
            wordpress_id
          }
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const BlogPosts = result.data.allWordpressPost.edges
  BlogPosts.forEach(post => {
    createPage({
      path: `/${post.node.slug}`,
      component: BlogPostTemplate,
      context: {
        id: post.node.wordpress_id,
      },
    })
  })
}

これで先ほど作ったBlogPostTemplateコンポーネントを使用して、記事が生成されます。

BlogPost.jsやBlogPostTemplateの名前が違っているとうまく生成されないので注意です。

ちなみに仮のWordPress環境ではこのように3つの記事を投稿しています。

ちゃんと記事が生成されているかは存在しないページに行くとわかります。

hello-world・test1・helloの3つの記事がちゃんと取れていますね

/test1などに飛ぶと先ほど作ったBlogPost.jsの中のBlogPostTemplateコンポーネントを元にページが生成されているのがわかります。

次に固定ページを作ります

templateディレクトリの中にPage.jsを作ります。

Page.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout.js"
import SEO from "../components/seo"
const PageTemplate = ({ data }) => (
  <Layout>
    <SEO
      title={data.wordpressPage.title}
      description={data.wordpressPage.excerpt}
    />
    <h1>{data.wordpressPage.title}</h1>
    <div dangerouslySetInnerHTML={{ __html: data.wordpressPage.content }} />
  </Layout>
)
export default PageTemplate
export const query = graphql`
  query($id: Int!) {
    wordpressPage(wordpress_id: { eq: $id }) {
      title
      excerpt
      content
    }
  }
`

BlogPost.jsみたいな感じでこれが固定ページのテンプレートになります。

次にgatsby-node.jsにpageを生成するコードを追加していきます。

gatsby-node.js
/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */

// You can delete this file if you're not using it

const path = require(`path`)
// const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
  const PageTemplate = path.resolve("./src/templates/Page.js")
  const result = await graphql(`
    {
      allWordpressPost {
        edges {
          node {
            slug
            wordpress_id
          }
        }
      }
      allWordpressPage {
        edges {
          node {
            slug
            wordpress_id
          }
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const BlogPosts = result.data.allWordpressPost.edges
  BlogPosts.forEach(post => {
    createPage({
      path: `/${post.node.slug}`,
      component: BlogPostTemplate,
      context: {
        id: post.node.wordpress_id,
      },
    })
  })

  const Pages = result.data.allWordpressPage.edges
    Pages.forEach(page => {
      createPage({
        path: `/${page.node.slug}`,
        component: PageTemplate,
        context: {
          id: page.node.wordpress_id,
        },
      })
    })
}

簡単ですね
先ほどのBlogPostでやったことと同じなのですぐにできます。

仮のWordPress環境ではこんな感じで「Sample」「test」の2つページが公開されています。

再び存在しないページに飛ぶと、sampleページとtestページが生成されているのがわかります。

/testに飛ぶと先ほど作ったPage.jsを元に固定ページが生成されているのがわかります。

トップページに記事一覧を表示する

pages/index.jsを編集していきます。

index.js
import React from "react"
import { graphql, Link } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const IndexPage = ({ data }) => (
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `test`, `sample`]} />
    <ul>
      {data.allWordpressPost.edges.map(post => (
        <li key={post.node.id}>
          <Link to={`/${post.node.slug}`} >
            <Img
              fluid={post.node.featured_media.localFile.childImageSharp.fluid}
              alt={post.node.title}
            />
            <div>
              <h3 dangerouslySetInnerHTML={{ __html: post.node.title }} />
              <span>{post.node.date}</span>
              <div dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
            </div>
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)

export default IndexPage

export const query = graphql`
  query {
    allWordpressPost(sort: {order: DESC, fields: date}) {
      edges {
        node {
          title
          excerpt
          slug
          date(formatString: "MMMM DD, YYYY")
          featured_media {
            localFile {
              childImageSharp {
                fluid(maxWidth: 1000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    }
  }
`

これで記事の一覧が表示されるかと思います。

*WordPress側でアイキャッチを設定していないと画像が取得できないのでエラーが出ます。(条件分岐する必要あり)

条件分岐の例:

index.js
import React from "react"
import { graphql, Link } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const IndexPage = ({ data }) => (
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `test`, `sample`]} />
    <ul>
      {data.allWordpressPost.edges.map(post => (
        <li key={post.node.id}>
          <Link to={`/${post.node.slug}`} >

            {post.node.featured_media && (
              <div>
                <Img
                  fluid={post.node.featured_media.localFile.childImageSharp.fluid}
                  alt={post.node.title}
                />
              </div>
            )}

            <div>
              <h3 dangerouslySetInnerHTML={{ __html: post.node.title }} />
              <span>{post.node.date}</span>
              <div dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
            </div>
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)

export default IndexPage

export const query = graphql`
  query {
    allWordpressPost(sort: {order: DESC, fields: date}) {
      edges {
        node {
          id
          title
          excerpt
          slug
          date(formatString: "MMMM DD, YYYY")
          featured_media {
            localFile {
              childImageSharp {
                fluid(maxWidth: 1000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    }
  }
`

上のように&&を使って条件分岐できます

サイトのメタデータを編集

gatsby-config.jsにサイトの基本情報を書くところがあります。

gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  }
}

これらの情報もWordPressで設定しちゃいましょう

graphqlでwordpressSiteMetadataのnameとdescriptionを引っ張ってきます。

components/の中のheader.jsを編集していきます。

ここでコンポーネント内でqueryを使うために作られたStaticQueryに書き換えます。

header.js
import { StaticQuery, graphql, Link } from "gatsby"
import React from "react"
const Header = () => (
  <StaticQuery
    query={graphql`
      query {
        wordpressSiteMetadata {
          name
        }
      }
    `}
    render={data => (
      <header>
        <div>
          <h1>
            <Link to="/">
              {data.wordpressSiteMetadata.name}
            </Link>
          </h1>
        </div>
      </header>
    )}
  />
)
export default Header

ヘッダーにメニューを追加する

今現在、WordPress REST APIでのデフォルトではメニューは利用できません。

WordPressに「WP API Menus」というプラグインを入れるとWordPressのREST APIからmenuを引っ張ってこれるようになります。

管理画面のプラグインから検索しても見つかりませんので、上のリンク先からプラグインをダウンロードして直接インストールしてください。

*以下の記事によると、管理画面のプラグイン検索で見つかる「WP-REST-API V2 Menus」は良くないみたいです
Complete guide to WordPress menu sourcing in Gatsby

理由がわからなかったので、こっちでも試してみましたが、固定ページのスラッグがちゃんと取れなかったのと、urlがちょっとおかしかったのでとりあえず「WP API Menus」の方を使います。

WordPressの外観/メニューからメニューを作成する
今回はheaderに表示するのでこんな感じで「headerNav」という名前のメニューを作りました

gatsby-config.jsに2行追加します

gatsby-config.js
{
      resolve: "gatsby-source-wordpress",
      options: {
        baseUrl: "taroken.mixh.jp/wp/gatsby/",
        protocol: "https",
        hostingWPCOM: false,
        useACF: false,
        verboseOutput: false,
        includedRoutes: [
          "**/categories",
          "**/posts",
          "**/pages",
          "**/media",
          "**/tags",
          "**/taxonomies",
          "**/users",
          "**/*/*/menus", // <== メニューのエンドポイントを追加
          "**/*/*/menu-locations", // <== メニューのエンドポイントを追加
        ],
      },
    },

これでgatsby developをしてgraphqlを見てみるとメニューのデータが引っ張ってこれているのがわかります

wordpressWpApiMenusMenusItemsのitemsから固定ページのスラッグやタイトルが取得できます

*WP-REST-API V2 Menusだとこのスラッグ(object_slug)が出てきませんでした(2019/09/15時点)

あとはcomponentsの中のheader.jsで引っ張ってくるだけです

header.js
import { StaticQuery, graphql, Link } from "gatsby"
import React from "react"
const Header = () => (
  <StaticQuery
    query={graphql`
      query {
        wordpressSiteMetadata {
          name
        }
        wordpressWpApiMenusMenusItems(name: { eq: "headerNav" }) {
          items {
            title
            object_slug
          }
        }
      }
    `}
    render={data => (
      <header>
        <div>
          <h1>
            <Link to="/">
              {data.wordpressSiteMetadata.name}
            </Link>
          </h1>
          <ul>
            {data.wordpressWpApiMenusMenusItems.items.map(item => (
              <li key={item.object_slug}>
                <Link to={`/${item.object_slug}`}>
                  {item.title}
                </Link>
              </li>
            ))}
          </ul>
        </div>
      </header>
    )}
  />
)
export default Header

こんな感じでheaderNavに追加してある固定ページのタイトルとリンクが取れました

これでベースの構成が完成です!

お疲れ様です!

とりあえずgatsby / WordPress REST APIで「記事の一覧」「記事ページ」「固定ページ」「メニューの取得」までできましたね

今後は以下を実装+解説していきますのでお楽しみに!

  • ページネーションの実装
  • カテゴリーページの実装
  • 記事のprevious / nextボタンの実装
  • ダークテーマの簡単実装
  • Tweenmaxでアニメーションさせる
  • ページ遷移

スタイリングは全くしていないので見た目はキモイですが、gatsby + WordPress REST APIのサイト制作の第一歩が出せたかと思います^ ^

ではまた次回〜〜^ ^

Share if you like

Commentなんでも気軽にどうぞ^ ^

Author
Web Designer

Kentaro Koga

福岡を拠点のクリエイターチーム「Fump」として活動をしています。

写真や動画の撮影やモノ作りが好きで、趣味は旅・カメラ・テニス・筋トレ・サウナ・レザークラフトです。

twitterアイコンinstagramアイコンfacebookアイコン