PR
【GatsbyJS製ブログ】GatsbyにおけるGraphQLの基本を丁寧に説明
今回はGatsbyの目玉、GraphQLについてです。
このGraphQL、かなり便利なのですが「Gatsbyで扱うGraphQL」という意味で色々と使うときに注意する点があります。
そのあたりの解説をしながら、GatsbyでのGraphQLの扱い方について説明していきます。
GraphQLとは?
そもそもGraphQLとは「Facebook社が開発・提供しているWeb APIのクエリ言語」だそうです。
実際にGraphQLを使っていて感じるメリットとしては、
柔軟性の高いリクエスト
これが大きいです。
一度に色々なデータへのリクエストができるので、必要な情報を一気に持ってくることができます。
逆に必要でないデータを無駄に持ってくることもないので(他のクエリ言語では不必要な情報まで取得してしまうこともある)、データ通信量やメモリー的にも有利です。
とりあえずGatsbyの中でGraphQLを使おう、というくらいなら、そこまで深くGraphQLを理解しなくても大丈夫です…。本当はちゃんと勉強したいところですが…。
GatsbyにおけるGraphQLの役割
では、GatsbyでGraphQLはどのように使われているのでしょうか?
こちらの記事で説明をしましたが、もう一度ブログを立ち上げるときのページ作成の概要図を載せておきます。
上の図を見て欲しいのですが、要はビルドの際に各記事の重要な情報を取ってきて、整理してJSファイルに渡しているのがGraphQLなのです。
最初は何をしているかわかりにくいGraphQLですが、Gatsbyの中で実際にどのようにGraphQLが動いているのかを説明していきます。
Gatsbyにおける2通りのGraphQLの扱い方
Gatsbyにおいて、GraphQLの使い方は主に2通りになります。
- gatsby-node.js、/src/pagesにある固定ページを作るJSファイル、/arc/templateにあるテンプレートJSファイルの中で扱うGraphQLのクエリ。このクエリの結果、得られたデータはそのJSファイルのdataプロップスに入っていきます。
正確には、gatsby-node.jsでのGraphQLの取得データはdataプロップスとして入ってはいかないのですが…。動的なページ作成のためのデータ取得(useStaticQueryを使ったGraphQLのクエリではない)、という意味でこちらの使い方になります。
- コンポーネント内などで、useStaticQueryを使ってGraphQLのデータを取得する。
最初は両者の違いがわかりにくいと思いますが、正直②のuseStaticQueryは(個人的には)あまり使わないかな、と思います。
基本的に①の使い方でデータ取得をしてページをビルドしており、どうしてもそれ以外のコンポーネント内で使いたいな(例えばSEOの設定などでサイトのタイトルやディスクリプションがいる、など)というときに②のuseStaticQueryを使います。
2つの違いは文章ではなんとも説明しにくいのですが、GraphQLの呼び出しの形を見てくれれば一目瞭然です。なので、とりあえず「GatsbyのGraphQLの使い方は大まかに2つあるんだな」と思っておいてください。ここから具体的に説明していきます。
GraphQLの使い方の基本
それでは、GraphQLの基本的な使い方を押さえていきましょう!
GatsbyJSにおけるGraphQLの書き方
Gatsbyで、GraphQLは基本的に次のように書いていきます。
import { graphql } from "gatsby" //gatsbyからgraphqlをインポートしておく
…
(ここらへんの書き方は先ほどの2つの呼び出し方によって異なる) = graphql`
query {
(ここに欲しい情報を入れ子で書いていく)
}
`
queryの後に名前を入れたりもできます。この名前にどんな意味があるかは正直ナゾです。入れても入れなくてもちゃんと動きます。
(ここらへんの書き方は先ほどの2つの呼び出し方によって異なる) = graphql`
query MyQuery {
(ここに欲しい情報を入れ子で書いていく)
}
`
例えば、サイトのタイトルとディスクリプションが欲しいときは次のように書きます。
import { graphql } from "gatsby" //gatsbyからgraphqlをインポートしておく
…
(ここらへんの書き方は先ほどの2つの呼び出し方によって異なる) = graphql`
query {
site {
siteMetadata {
title
description
}
}
}
`
この引っ張り出してきたデータをどう使うか、はこの後解説していきます。
GraphiQLを使う
欲しい情報を入れ子で書いていくって…どうやって書いてくの?さっきのサイトのタイトルとかディスクリプションの取得って、なんであんな構造になってるの?
と思う人もいるかもしれません。
これらは、実はGraphiQLというツールを使って調べることができます。
gatsby developをした状態で、試しにブラウザのURLに「localhost:8000/___graphiql」と入力してみてください。すると、次のような画面が開くはずです。
私の環境では、MDXなんか入れちゃってるのでいきなりこんな画面になりますが、似たような画面が立ち上がっていればOKです。
このGraphiQLというツールを使って、左側のExploreから欲しい情報を探し出してきます。すると、真ん中に自動的にquery以下の入れ子構造が表示されます。
これを先ほどのquery以下の部分に書いてあげるだけです。
ちなみに、GraphiQLの真ん中の再生ボタンを押せば、実際にどんな情報が取得できるかを試してみることができます。
これで欲しいデータが取得できるか?を確認できます。
テンプレートでGraphQLを使う
ここからが本題です。テンプレートのJSファイルでGraphQLを使う例を見ながら、GraphQLの形と使い方を押さえていきましょう。
これが先ほどのGatsbyにおけるGraphQLの使い方の①ですね。いくつかポイントがあるので、そこも解説していきます!
例えば、GraphQLを使って、マークダウンで作ったブログ記事データを拾ってきて、実際にブログページを作るという流れを使って解説していきます。
ここでは、nodeを作成する設定ファイル「gatsby-node.js」と、実際にブログ記事を作るテンプレートである「blog-post.js」を使います。
具体的には、
- gatsby-node.jsでGraphQLを使ってデータを拾ってくる。
- その情報をblog-post.jsに渡す。
- さらに、もらった情報を元にblog-post.jsでGraphQLを使って、データを拾う。
となります。
これはgatsby-starter-blogなんかを使って立ち上げたブログでの実際の流れになります。要所を抜粋しながら説明していきます。
次のソースはgatsby-node.jsを一部抜粋したものです。
…
//↓ ※① とりあえず全てのマークダウンのデータからid、slugを取得します。
//↓ GraphQLの書き方は次のように書きます。
const result = await graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
fields {
slug
}
}
}
}
`
)
const posts = result.data.allMarkdownRemark.nodes // ※① その結果達をとりあえずpostsに入れます。
…
posts.forEach((post, index) => { // postsは全てのマークダウンのデータが入ったJSONなので、一つ一つをブログ記事のページにするためにforEachを使う
…
createPage({ // createPageについては別記事参照
path: post.fields.slug, // pathには、slugを渡します。
component: blogPost, // componentには、使うテンプレートを渡します。(ここの記述は省略しているので「blogPostとは?」というのは気にしなくていいです。)
context: { // ※② contextは、渡した先(blog-post.js)で直接GraphQLの変数としても使えるし、propsの「pageContext」としても引っ張り出せます。
id: post.id, // ※② idというpropsにブログ記事のIDを渡します。
…
},
})
})
…
次のソースはblog-post.jsを一部抜粋したものです。
…
const BlogPostTemplate = ({ data, location }) => {
// ※③ ↑propsのdataにGraphQLの取得結果が入ります。このデータは、このファイルの下部のGraphQLで取得したものです。
const post = data.markdownRemark // ※③ このようにしてマークダウンのデータを代入することがででます(別に代入する必要はありませんが、代入しとかないと、いちいち長ったらしく書かないといけないので)
…
return (
…
<h1>{post.frontmatter.title}</h1> // こんな感じで取得したデータ(タイトルや日付など)を使うことができます。
<p>{post.frontmatter.date}</p>
<section
dangerouslySetInnerHTML={{ __html: post.html }} // マークダウンでは、「html」がいわゆる記事の本体になります。こんな感じで記事本体をHTMLに起こします。
itemProp="articleBody"
/>
…
)
}
export const pageQuery = graphql`
query BlogPostBySlug(
$id: String // ※② こんな感じでgatsby-node.jsのcontextから渡された変数を使うことができます。
…
) {
…
markdownRemark(id: { eq: $id }) { // ※② gatsby-node.jsからもらった$idとIDが等しい記事のデータだけ持ってきます。
id
html
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
description
}
}
…
}
`
- gatsby-node.jsでGraphQLを使い、全てのマークダウンからIDとslugを取得します。これをテンプレートに順番に渡していくことで、いわゆる「動的にページを作成」していきます。
- ここがテンプレートでGraphQLを使う際の最大のポイントです。このcontextでテンプレートに渡したデータは、テンプレート側でGraphQL検索条件の変数として使えます。
例えば「このIDと同じブログ記事からタイトルと投稿日とディスクリプションを取ってきて!」ということができます。このように検索条件を変数で指定できるのは、このテンプレートでGraphQLを使う方法だけです!
- GraphQLで拾ったデータはテンプレートのdataというpropsに入っていきます。
gatsby-node.js内のcreatePageについてはこちらの記事で詳しく説明しています。
ちなみに、このgatsby-node.jsで渡したcontextは、テンプレートの方でGraphQLに使うだけでなく、pageContextというpropsとしても渡っていくので、GraphQL以外のテンプレート内部でも使うことができます。
ちなみに私のブログでは、ページネーションのページを作るときにこのpageContextを使っています。「このページが全ページの中の何ページ目?」というのを判断するときに使っています。
component内でuseStaticQueryを使う
こちらはテンプレートなどでGraphQLを使うよりも簡単、シンプルです。
簡単なのですが、変数などをGraphQLの絞り込み条件に使えないのが難点です。
useStaticQueryを使うときは、とりあえずGraphQLで必要な情報を含むデータを引っ張ってきて、プログラミング的にその情報をふるいにかける、という操作が必要になってきます。
それでは具体例を見ていきましょう。
import React from 'react';
import { useStaticQuery, graphql } from 'gatsby'; //useStaticQueryとgraphqlをインポート
import * as styles from '../styles/contentscard.module.scss'/
const ContentsCard = ({ id }) => {
const data = useStaticQuery(graphql` // こんな感じでuseStaticQueryを使います。
query {
allMarkdownRemark(sort: {frontmatter: {date: DESC}}) { //とりあえず全記事のデータを拾ってきます。絞り込む条件がわかっていればいいですが、propsのような変数は条件の絞り込みとして使えません。
edges {
node {
id
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
description
}
}
}
}
}
`)
const posts = data.allMarkdownRemark.edges.node
let post
for (let i = 0; i < posts.length; i++){
if(id == posts[i].id){ // こんな感じで、propsから渡ってきた変数で絞り込むには、if文とかを使うしかないです。
post = posts[i]
}
}
…
これは、私のブログでブログカードを表示するコンポーネントの一部です。(本当はMDXを使っているのでallMarkdownRemarkではなくallMdxですが…)
useStaticQueryは上記のような書き方をしてくれればコンポーネント内部でもGraphQLを使うことができます。
テンプレートでGraphQLを使うときよりもシンプルですね。ですが、本当はGraphQLでデータを拾うときに、該当記事のIDを使ってその記事の「タイトル」「ディスクリプション」のみを取得したいところですが、それができないのが難点です。
propsなんかを使って該当記事のデータのみを取得したければ、とりあえず全マークダウンのデータを引っ張ってきて、このソースの最後のようにif文とかを使って絞り込むしかないですね。
まとめ
GatsbyにおけるGraphQLの扱い方の基本についてまとめました。
動的にページを作っていく過程で「テンプレートに変数を渡してGraphQLで使う」という部分がGatsby独特なので、最初はわかりにくいと思います。
GraphQLはGatsbyの目玉機能の一つなので、ぜひ使いこなしていきたいですね。
ちょっとしたコツはいりますが、慣れればかなり便利ですよ。
Gatsbyを学習している方への私からのオススメ!
私が読んでよかったな、と思うGatsbyの学習をするのにオススメの本を紹介しておきます。
画も多くてとても読みやすく、ステップバイステップでGatsbyJSのサイトを作ることができます!
また、Gatsbyを学習する上で、Reactを同時に学習することもオススメします。
こちらも、まず構成や見た目がとても読みやすい本です。加えてReactに対しての専門的な知識を丁寧に学ぶことができます。ここでReactに対する基礎知識をしっかりしておくと、Gatsbyに対する見方が変わって、Webページをサクサク作ることができるようになります!
あと、色々と調べたのですが、Reactの学習ならUdemyもオススメです。必要な知識を1動画単位で購入できるので、学習に無駄がないです。Reactってあんまり学習できるプログラミングスクールみたいなのがないんですよね…。「React」と検索するだけで大量に動画がでてきます!