エンジニアを目指す日常ブログ

日々勉強したことのメモ。独学ですので間違っていたらコメント等で教えてください。

VirtualBoxの容量不足エラーが出たときにやったことメモ

はじめに

VirtualBoxで突然以下のようなエラーが発生。

npm ERR! code ENOSPC
npm ERR! syscall write
npm ERR! errno -28
npm ERR! nospc ENOSPC: no space left on device, write
npm ERR! nospc There appears to be insufficient space on your system to finish.
npm ERR! nospc Clear up some disk space and try again.

容量が足りないエラーのようで、不要ファイルを削除することで解消した。 実施したことをメモする。

追記

本記事を書いた数時間後に再度同じエラーが発生、どうしようもなくなったため容量拡張を実施した。 あくまで本記事は暫定対応と考えたほうがよい。

前提

  • Windows環境の上に、VirtualBox + Vagrant で構築したLinux環境を利用。
  • React.jsのGatsbyモジュールを使ったアプリを開発していた時に起こった。
  • OSはubuntuを利用。
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic

原因の確認→ディスク容量不足

このエラー発生時は、ディスク容量が足りない可能性が高い。 参考にした記事では、innodeが足りなかったとのこと。

参考: No space left on device とエラーが出るときの対処法 - Qiita 自分の環境でたたいてみると、ディスク容量が足りないことが分かった。

$ df -hP
Filesystem              Size  Used Avail Use% Mounted on
udev                    1.5G     0  1.5G   0% /dev
tmpfs                   299M  640K  299M   1% /run
/dev/sda1               9.7G  9.7G     0 100% /
tmpfs                   1.5G     0  1.5G   0% /dev/shm
tmpfs                   5.0M     0  5.0M   0% /run/lock
tmpfs                   1.5G     0  1.5G   0% /sys/fs/cgroup
vagrant                 238G  199G   39G  84% /vagrant
home_vagrant_workspace  238G  199G   39G  84% /home/vagrant/workspace
tmpfs                   299M     0  299M   0% /run/user/1000

/dev/sda1というのは、10GBであることからおそらくVirtualBoxのメインストレージ。

VirtualBoxマネージャー画面
VirtualBoxマネージャー画面

ディスク容量をひっ迫しているフォルダの確認

ルートディレクトリ配下のフォルダごとのデータ量を確認する。

ルートフォルダに移動して中身の確認。

$ cd /
$ sudo du -sh ./*/
8.1G    ./home/
...省略...

ホームディレクトリが圧倒的に大きいことが分かった。

ホームディレクトリの中のvagrant(ユーザ名)ディレクトリに移動して、隠しファイル以外のデータ量を確認する。

$ cd home/vagrant
$ sudo du -sh ./*/
161M    ./aws/
753M    ./gatsby/
4.0K    ./tmp/
2.5G    ./workspace/

これだけでは8.1GBに達する理由がわからないため、隠しフォルダのデータ量も確認。

$ du -scmh ./.[^.]* | sort -rh
4.7G    total
1.3G    ./.nvm
1.3G    ./.cache
1.1G    ./.npm
939M    ./.vscode-server
116M    ./.config
5.2M    ./.yarn
...省略...

参考: duでカレントディレクトリ以下の使用容量をパッと見たいとき用のコマンド 隠しファイルも - Qiita

1.3G    ./.nvm
1.3G    ./.cache
1.1G    ./.npm

このあたりの容量が大きそうだが、nvmnpmは消したらまずい気がしたのでcacheをターゲットにする。

キャッシュフォルダの中身確認

./.cacheフォルダの中身を確認する。

$ cd ./.cache
$ sudo du -sh ./*/
109M    ./typescript/
1.2G    ./yarn/

どうやら./yarnフォルダが容量を食っている模様。

yarnのキャッシュを消してよいのか調査

home/.cache/yarnフォルダを消して問題が起きないか自信が無い(さすがにキャッシュだから大丈夫だとは思うが)ので少し調べたところ、以下の記事で気軽にキャッシュをクリアしていたので、問題ないと信じることにした。

参考: 【yarn install】ハマったときの解決方法

どのプロジェクトで使われているフォルダなのか

作業していたGatsby.jsのプロジェクトフォルダに移動して、yarnのキャッシュ保管フォルダを確認すると、まさに問題になっていたフォルダだった。

$ cd gatsby-starter-blog/
$ yarn cache dir
/home/vagrant/.cache/yarn/v6

参考: yarnのキャッシュを理解してnode_modulesを正しく更新する - さかなソフトブログ

このまま

$ yarn cache clean

を実施したところ、データ量が1GBほど削減された。

VartualBox再起動

再起動すると、Linuxの操作が再びできるようになった。

vagrant halt
vagrant up

おわりに

VirtualBoxの容量不足エラーを、キャッシュを削除することで回避できた。

ただし、本来はVirtualBoxへの割り当て容量を増やすほうが良いと思う。

また、本当に容量が足りなくなるとvagrant upすらできなくなるので、詰む可能性あり。 私はwindowsと共有しているフォルダから適当なファイルを手動で削除することで、起動できた。

【Gatsby】マークダウンファイルから記事ページを生成する

はじめに

前回、マークダウンのファイル情報をデータレイヤに取り込み、ブログ上に表示することができた。

今回はブログ記事ページを動的に作成する。

方法としては、

がありそう。

今回はテンプレートコンポーネントを作成する方法を利用する。

本記事のゴール

  • ブログ記事ページのテンプレートを作成する。
  • 開発環境に保存したマークダウン形式の記事の中身と、テンプレートから、記事ごとのページを自動で生成する。

前回の記事

tomiko0404.hatenablog.com

前提

  • gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
  • Vagrantを開発環境として利用
  • Gatsby Cloudで公開済(今回は利用しないので、なくても可)
  • gatsby-source-filesystemの設定が完了し、/content/blogフォルダ内のファイルがデータレイヤーに読み込まれる状態になっている
  • gatsby-plugin-mdxの設定が完了し、ファイルをMDXファイルとして利用できる状態になっている

参考にしたサイト

www.gatsbyjs.com

テンプレートとマークダウンから動的にページを作成する流れ

  1. マークダウンファイルを作成する
  2. テンプレートコンポーネントを作成する
  3. テンプレートコンポーネント内で、特定の(URLに対応する)マークダウンから情報を読み込むソースコードを記述する
  4. ビルドする

マークダウンファイルを作成する

前回の記事を参考に、MDXファイルを作成する。

content/blog/mdx-content.mdx

---
title: "mdxで記事を書いてみる"
date: "2022-02-26"
---

## はじめに
記事の内容

テンプレートコンポーネントを作成する

テンプレートコンポーネントを作成する。

作成する場所

pages配下の、URLに対応する場所に作成する。

例えば、ブログ記事ページのURLを~~~~.com/blog/記事名にしたい場合、テンプレートコンポーネントsrc/pages/blog/配下に作成する。

コンポーネントのファイル名

{mdx.slug.js}とする。

構文としては

{{データレイヤのノード名}.{URLに利用したいフィールド名}.js}

となる。

今回は、mdxノードを利用し、URLにはslugフィールドを利用するので{mdx.slug.js}となる。

別のフィールドを使いたい場合

{URLに利用したいフィールド名}が深い場所にある場合は、__(アンダーバー2つ)でネストして行ける。(例:{mdx.frontmatter__slug}.js

slugフィールドとは

gatsby-plugin-mdxを使ってMDXノードに変換すると、自動的にslugフィールドが生成される。これをURLに利用するとよい。

slugフィールド
slugフィールド

自分でslugを設定する

MDXファイル内に自分でslugフィールドを作成し、ファイル名を

{mdx.frontmatter__slug}.js

とすると、ファイル名とは別に好きなURLにすることができる。

---
title: "mdxで記事を書いてみる2"
slug: "mdx-content-my-slug"
date: "2022-03-07"
---

## はじめに

テンプレートの中身を記載する

テンプレートコンポーネント内で、特定の(URLに対応する)マークダウンから情報を読み込むソースコードを記述する。

クエリの作成

クエリはテンプレートコンポーネントに以下のように記載する。

export const query = graphql`
query ($id: String) {
    allMdx(filter: {id: {eq: $id}}) {
      nodes {
        body
        slug
        frontmatter {
          date(formatString: "MMMM D, YYYY")
          title
        }
      }
    }
  }
`
query ($id: String)

は、id変数を引数として利用するという宣言。

allMdx(filter: {id: {eq: $id}}) {

は、idフィールドが$idと等しいもののみ抽出するという指定。

参考:変数を引数としてクエリに設定する方法のメモ tomiko0404.hatenablog.com

変数に入れる値の取得方法

クエリえMDXノードの情報を取得する際に「どのページか」を判定する必要がある。

Gatsbyでは、ページテンプレートコンポーネントを使って作られたページにおいて、props.pageContextの中に、MDXノードを一意に特定するidの値が初めから入っているためこれを利用する。

props.pageContextにはidと、URLに利用したいフィールド名(ここではslug)が自動で渡される。

以下ページによると、ページ生成時にprops.pageContextとして渡された値はGraphQLの引数としてそのまま利用できる。

// Add optional context data to be inserted
// as props into the page component.
//
// The context data can also be used as
// arguments to the page GraphQL query.
//
// The page "path" is always available as a GraphQL
// argument.
Gatsby Node APIs | Gatsby

そのため、前節のクエリを記載するだけで、ページに特有のidをフィルタ条件として利用してくれる。

pageContextについては勉強不足のため割愛する。

テンプレートコンポーネントソースコード

全体としてソースコードは以下のようになる。

src/pages/blog/{mdx.slug}.js

import { graphql } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import * as React from 'react';
import MyLayout from '../../components/myLayout';


const BlogPost = (props) => {
    console.log(props);
    const {data} = props;
    return (
        <MyLayout pageTitle="Super Cool Blog Posts">
            <MDXRenderer>
                {data.allMdx.nodes[0].body}
            </MDXRenderer>
        </MyLayout>
    )
}


export const query = graphql`
query ($id: String) {
    allMdx(filter: {id: {eq: $id}}) {
      nodes {
        body
        slug
        id
        frontmatter {
          date(formatString: "MMMM D, YYYY")
          title
        }
      }
    }
  }
`

export default BlogPost

今回は勉強の流れでallMdxフィールドを利用し、idを指定することで要素数1の配列を取得・表示している。

1件のMDXノード情報だけ取れればよいので、mdxフィールドを利用するのがよさそう。

おわりに

テンプレートコンポーネントを作成して、ブログ記事ページを自動で生成することができた。

GraphQLの引数を変数で設定する方法のメモ

はじめに

GraphQLのリクエストに引数を入れる際、引数を固定値ではなく変数で設定するやり方をメモ。

GraphQLとは/基本的なリクエストの書き方

以下の記事を参照。 tomiko0404.hatenablog.com

引数なしのリクエス

リクエスト(すべてのMDXノードのslug(ファイル名)フィールドとidフィールドを取得)

query {
  allMdx {
    nodes {
      slug
      id
    }
  }
}

取得できる値

"data": {
 "allMdx": {
   "nodes": [
     {
       "slug": "mdx-content",
       "id": "c31f21f8-8731-5bc1-b8fa-906aabd57930"
     },
     {
       "slug": "mdx-content2",
       "id": "9ce566d8-ae20-5b54-9d23-424487f61361"
     }
   ]
 }

引数が固定値のリクエス

リクエスト(MDXノードのうちid"9ce566d8-ae20-5b54-9d23-424487f61361"と等しいノードのslug(ファイル名)フィールドとidフィールドを取得)

query {
  allMdx(filter: {id: {eq: "9ce566d8-ae20-5b54-9d23-424487f61361"}}) {
    nodes {
      slug
      id
    }
  }
}

取得できる値

"data": {
  "allMdx": {
    "nodes": [
      {
        "slug": "mdx-content2",
        "id": "9ce566d8-ae20-5b54-9d23-424487f61361"
      }
    ]
  }
}

引数に変数を設定する場合のリクエス

この方法はページクエリでしか利用できない。

useStaticQueryでは利用できないので注意する。

リクエスト(MDXノードのうちidフィールドが変数idと等しいノードのslug(ファイル名)フィールドとidフィールドを取得)

query ($id: String) {
    allMdx(filter: {id: {eq: $id}}) {
      nodes {
        slug
        id
      }
    }
  }

GraphiQL

GraphiQLを使ってリクエストを作成する際は、変数に入る値を仮設定することができる。左下のQUERY VARIABLESにJSON形式で設定する。

GraphiQLでの変数仮設定
GraphiQLでの変数仮設定

Gatsby.jsでの利用

MDX(マークダウン)ファイルの情報が入っているMDXノードの情報を取得するために、テンプレートとなるコンポーネント上でidを指定するクエリが必要である。

Gatsbyでは、ページテンプレートコンポーネントを使って作られたページにおいて、props.pageContextの中に、MDXノードを一意に特定するidの値が初めから入っている。

idを変数として引数に入れておくことで、props.pageContext.idの値をフィルタ条件として利用できる。

{mdx.slug}.js

import { graphql } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import * as React from 'react';
import MyLayout from '../../components/myLayout';

const BlogPost = (props) => {
    console.log(props.pageContext.id);
    const { data } = props;
    return (
        <MyLayout pageTitle="Super Cool Blog Posts">
            <MDXRenderer>
                {data.allMdx.nodes[0].body}
            </MDXRenderer>
        </MyLayout>
    )
}

export const query = graphql`
query ($id: String) {
    allMdx(filter: {id: {eq: $id}}) {
      nodes {
        body
        slug
        frontmatter {
          date(formatString: "MMMM D, YYYY")
          title
        }
      }
    }
  }
`

export default BlogPost

pageContextについては勉強不足のため割愛する。

おわりに

GraphQLで引数を利用し、props等から取得した変数を設定する方法をメモした。

【Gatsby】マークダウンファイルの情報を取得してブログ上に表示する

はじめに

前回に引き続き、開発環境にマークダウン形式の記事を保存し、Gatsbyで取得する方法を確認する。

今回は取得したファイルの中身(マークダウン形式)を取得して、解析して最終的にHTMLに変換して表示する方法を確認する。

ここまで参考にしてきたGatsby公式チュートリアルと、gatsby-starter-blogスターターキットとでは、利用するマークダウンの変換方法が異なる。

参考にした方法 ファイル形式 変換プラグイン
公式チュートリアル .mdx gatsby-plugin-mdx
gatsby-starter-blogスターター .md gatsby-transformer-remark

本記事ではmdx形式を利用できるGatsby公式チュートリアルのやり方で実施する。

本記事のゴール

  • 開発環境に保存したマークダウン形式の記事の中身を取得する。
    • 開発環境からデータレイヤーに情報を送信する。
    • データレイヤ内で、ノードの形式を「ファイルノード」から「MDXノード」に変換する。
    • データレイヤーにGraphQLでアクセスし、情報を取得する。
  • 取得したマークダウン記事をブログに表示する。

赤枠が本記事の実施範囲である。青枠が前回の実施範囲となる。

前回の記事

tomiko0404.hatenablog.com

前提

  • gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
  • Vagrantを開発環境として利用
  • Gatsby Cloudで公開済(今回は利用しないので、なくても可)
  • 前回の記事で実施した通り、gatsby-source-filesystemの設定が完了し、/content/blogフォルダ内のファイルがデータレイヤーに読み込まれる状態になっている

参考にしたサイト

www.gatsbyjs.com

MDXファイルとは

基本的にマークダウン形式のファイルだが、JSXの形式も書いて良いよ!という形式。

通常のマークダウンでは、マークダウンまたはHTMLしか書けないが、

console.log(""); // インデントするとJSとして動かないらしいので注意

<MyComponent title="あああ"></MyComponent>

のようなものを書くことができる。

詳細は以下サイトを参照。

What is MDX? | MDX

ファイルノード・MDXノードとは

データレイヤーに取り込まれたデータの1つ1つは「ノード」と呼ばれる.。

  • ノードの種類は、ソースプラグインごとに異なる。
  • ノードの種類によって持つフィールドは異なる。

sorce-filesystemプラグインを利用した場合、「ファイルノード」が作成される。

ファイルノードには、例えばファイル名を表すnameや、更新日時を表すmodifiedTime等のフィールドがあり、以下のようにクエリで取得することができる。ただし、マークダウンで書かれたファイルの中身を上手く取得できるようなフィールドは無い。

query {
  allFile {
    nodes {
      id
      name
    }
  }
}

そこで、「MDXノード」に変換する必要がある。

MDXノードは、body(本文)や、frontmatterに記載した内容、slug(ファイル名)等を取得することができる。

query MyQuery {
  allMdx {
    nodes {
      frontmatter {
        title
      }
      slug
      body
    }
  }
}

ファイルノードをMDXノードに変換する

MDXファイルの中身をつくる

MDXファイルに記事の内容を書く。 冒頭にはfrontmatterを記載することができる。

frontmatterの下にはマークダウン+JSX(今回は単なるマークダウン)で記事を記載する。

frontmatterとは

frontmatterとは、英単語で言うと「本の奥付」。

まず Front Matter とはなんぞや、というと、元々は Jekyll という静的サイトジェネレータが広めたらしい、文書のメタデータYAML で書く手法。 Markdown ファイルに限らず、そのファイルの冒頭で、以下のようにハイフン3つで囲んだ行の中が YAML 形式になっていて、その部分をメタデータ的に活用できるというモノ。
Remark プラグインを使って Markdown から Front Matter を抽出する - Neo's World

ということ。

マークダウンファイルの冒頭に以下のように記載する。

/content/blog/mdx-content.mdx

---
title: "mdxで記事を書いてみる"
date: "2022-02-26"
---

記事を取得した際、node.frontmatter.titlenode.frontmatter.dateとして値を取得できる。

また、上記のdateのように"xxxx-xx-xx"という形式のデータを入れておくと、GraphiQLで要求電文を作成するとき、formatStringという様々な日付形式に変換できるフィルターをサジェストしてくれるので便利。

プラグインの利用設定

インストール

gatsby-plugin-mdxをインストールする。

gatsby-plugin-mdx | Gatsby

$ npm install gatsby-plugin-mdx @mdx-js/mdx@v1 @mdx-js/react@v1

コンフィグファイル設定

gatsby-config.jsplugin配列の中に、以下のように記載を追加する。

plugins: [
        `gatsby-plugin-image`,
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                path: `${__dirname}/content/blog`,
                name: `blog`,
            },
        },
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                name: `images`,
                path: `${__dirname}/src/images`,
            },
        },
        // 省略
        `gatsby-plugin-mdx`,  // 追加
    ],

MDXノードの値を取得する

blog-tutorialコンポーネントで、MDXノードの値を取得する。

取得した値は、props.dataに格納されるため、コンポーネント上で表示する。

src/pages/blog-tutorial.js

import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import * as React from "react";
import MyLayout from "../components/myLayout";

const BlogTutorial = (props) => {
    const { data } = props;
    console.log(data);
    return (
        <MyLayout pageTitle="ブログ一覧">
            {data.allMdx.nodes.map((node) => (
                <article>
                    <h2>{node.frontmatter.title}</h2>
                    <p>Posted: {node.frontmatter.date}</p>
                    <MDXRenderer>
                        {node.body}
                    </MDXRenderer>
                </article>
            ))}
        </MyLayout>
    );
};

export const query = graphql`
    query {
        allMdx(sort: { fields: frontmatter___date }) {
            nodes {
                frontmatter {
                    title
                    date(formatString: "YYYY年MM月DD日")
                }
                id
                body
                parent {
                    ... on File {
                        modifiedTime
                    }
                }
            }
        }
    }
`;

各フィールドの説明

allMdxで取得できるフィールドを説明する。

フィールド 説明
frontmatter.title MDXファイルのfrontmatterにtitleタグをつけて記載した内容を取得。
frontmatter.date MDXファイルのfrontmatterにtitleタグをつけて記載した内容を取得。(formatString: "")オプションをつけると日付形式を指定できる。
リファレンス:Moment.js | Docs
id 各ノードを一意に識別するための文字列ID。<li>要素を使うときのキーにも利用できる。
body ファイルの中身からfrontmatterを除いたもの。
parent MDXに変換する前のノードを示す。ここでは、「対応するファイルノード」を示す。

MDXを読み込む

ファイルから取得したbodyはそのままでは読み込めない。

<MDXRenderer>コンポーネントとセットで利用する必要がある。

上記ソースコードの通り、

import { MDXRenderer } from "gatsby-plugin-mdx";

でインポートして、

<MDXRenderer>
    {node.body}
</MDXRenderer>

で囲む。

結果表示

localhost:8000/blog-tutorialページに、MDX記事の内容を表示できた。

記事の内容を表示できた
記事の内容を表示できた

【参考】他に必要なプラグイン

公式チュートリアルで紹介されていた、記事作成に必要なプラグインは以下。

他にも gatsby-remark-prismjs-add-title(コードブロックの頭にファイル名を表示する)等も利用できそう。

おわりに

マークダウンで作成した記事のデータを取り込み、GraphQLで取得してコンポーネントで表示することができた。

まだ、記事ごとのページは作成できていないので、次回以降確認する。

関連記事

つづき

【Gatsby】プロジェクト内に保管したファイルの情報をGraphQLで取得する

はじめに

Gatsbyでブログを書くためには、1ページずつHTMLを書くわけにもいかないので、記事を書いたデータベースからデータを取得してコンポーネントに組み込む必要がある。

開発環境にマークダウン形式の記事を保存し、Gatsbyで取得する方法を確認する。

マークダウンの解析は次回とし、今回はファイルの情報を取得するところまで実施する。

この記事のゴール

  • 開発環境に保存したマークダウン形式の記事の「ファイル名」等のファイル情報を取得する。
    • 開発環境からデータレイヤーに情報を送信する。
    • データレイヤーにGraphQLでアクセスし、情報を取得する。
  • 取得した情報をサイトに表示する。

以下の図の赤字部分が本記事の範囲である。

前回の記事

tomiko0404.hatenablog.com

前提

  • gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
  • Vagrantを開発環境として利用
  • Gatsby Cloudで公開済(今回は利用しないので、なくても可)

参考にするサイト

Part 4: Query for Data with GraphQL | Gatsby

今回の流れ

開発環境のcontent/blogフォルダにマークダウン形式の記事を配置し、配置した記事をGraphQLデータレイヤに読み込ませる。

ブログのBlogTutorialコンポーネントから、GraphQL APIを利用してデータレイヤにアクセスしてデータを取得する。

取得したデータをコンポーネントに表示してみる。

マークダウンファイルの準備

content/blogフォルダ配下にブログ記事を用意する。 gatsby-starter-blogスターターキットでは、content/blogフォルダとその配下のサンプル記事(.md)形式が事前に用意されている。

content
└── blog
    ├── ay-page
    │   └── index.md
    ├── hello-world
    │   ├── index.md
    │   └── salty_egg.jpg
    ├── my-second-post
    │   └── index.md
    └── new-beginnings
        └── index.md

マークダウンファイルをGraphQLデータレイヤに読み込ませる

プラグインの利用設定

フォルダのマークダウン記事をGraphQLデータレイヤから読み込むには、gatsby-source-filesystemというプラグインが必要。

データレイヤーに関しては以下の記事を参照。

GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ

また、プラグインの使い方は以下の記事を参照。

Gatsbyブログでのプラグイン利用方法/画像の挿入方法 - エンジニアを目指す日常ブログ

gatsby-source-filesystemプラグインがインストールされていない場合は、こちらの記事を参考にnpm installする。

次に、gatsby-config.jspluginの中に以下記載する。(スターターキットを使っているので、既に記載されていた。)

plugins: [
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                path: `${__dirname}/content/blog`,
                name: `blog`,
            },
        },
・・・
],

オプションの意味は以下のようになる。

  • resolveに、プラグイン名を記載
  • ビルド時、pathで指定された${__dirname}/content/blog内のファイルをすべてGraphQLデータレイヤーに追加する。
  • ${__dirname}はNode.jsが保持している特殊な変数で、現在の場所の絶対パスを表す。
  • nameは、特定のフォルダを指定してデータを取得するために、別名として使用する。
// nameオプションの使い方
// このクエリの{eq: "blog"}のところで使う
query {
  allFile(filter: {sourceInstanceName: {eq: "blog"}}) {
    nodes {
      name
    }
  }
}

ちなみに、gatsby-config.jsファイルのpluginの配列要素は、

  • String型でプラグイン名だけ記載
  • オブジェクト型でオプションも含めて記載

の2通りの書き方がある。今回は「オブジェクト型」を利用していることになる。

データレイヤに読み込ませる

gatsby devlopgatsby buildを実行すると、gatsby-config.jsファイルの設定に従ってデータレイヤにデータが読み込まれる。

コンポーネントにクエリを追加する

要求電文の準備

以下の通りgatsby developを実行して、http://localhost:8000/___graphqlにアクセスする。

$ sudo sysctl fs.inotify.max_user_watches=524288
$ gatsby develop --host=0.0.0.0

参考:
Vagrant上でGatsbyブログのひな型を作成した - エンジニアを目指す日常ブログ
gatsby developで出たエラーの対処法 - エンジニアを目指す日常ブログ
GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ

以下の要求電文を作り、GraphiQL上で応答を確認する。

query MyQuery {
  allFile {
    nodes {
      name
      sourceInstanceName
    }
  }
}

/content/blog配下のファイル以外も返ってきている。

{
  "data": {
    "allFile": {
      "nodes": [
        {
          "name": "index",
          "sourceInstanceName": "blog"
        },
        // 省略
        {
          "name": "salty_egg",
          "sourceInstanceName": "blog"
        },
        {
          "name": "gatsby-icon",
          "sourceInstanceName": "images"
        },
        {
          "name": "profile-pic",
          "sourceInstanceName": "images"
        },
        {
          "name": "rPOmLGwai2w",
          "sourceInstanceName": "__PROGRAMMATIC__"
        }
      ]
    }
  },
  "extensions": {}
}

クエリ追加と結果確認

要求電文が準備できたので、blog-tutorialコンポーネントに追加する。

以下の記事に記載した通り、graphqlのクエリには以下の特徴があることに注意。

GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ

src/pages/blog-tutorial.js

import { graphql } from 'gatsby'
import * as React from 'react'
import MyLayout from '../components/myLayout'

const BlogTutorial = (props) => {
    const {data} = props;
    console.log(data);
    return (
        <MyLayout pageTitle="ブログ一覧">
        </MyLayout>
    )
}

export const query = graphql`
query {
    allFile {
      nodes {
        name
        sourceInstanceName
      }
    }
  }
`
export default BlogTutorial;

※先ほど準備した要求電文から名前(MyQuery)は削除したほうが良い。

実行するとコンソールでdataの中身を確認できる。

data変数の中身
data変数の中身

ファイル名と更新日時を表示してみる

クエリの書き換え

これまでのクエリだと、content/blogフォルダの記事以外のファイルも取得してしまうので、sourceInstanceNameを使ったfilter設定を入れる。

また、このあと表示する更新日時も取得するようにしておく。

export const query = graphql`
query {
  allFile(filter: {sourceInstanceName: {eq: "blog"}}) {
    nodes {
      name
      sourceInstanceName
      modifiedTime
    }
  }
}
`

blog-tutorialコンポーネントで表示

コンポーネントの中身は以下のように記載する。

const BlogTutorial = (props) => {
    const { data } = props;
    console.log(data);
    return (
        <MyLayout pageTitle="ブログ一覧">
            {data.allFile.nodes.map((node) => (
                <>
                    <p>ファイル名:{node.name}</p>
                    <p>更新日時:{node.modifiedTime}</p>
                </>
            ))}
        </MyLayout>
    );
};

表示結果
表示結果

おわりに

ローカルのファイルをデータレイヤに読み込ませるために、gatsby-source-filesystemプラグインの利用方法を覚えた。 また、データレイヤに読み込んだファイル情報をコンポーネントに表示した。

ファイルの情報はこのように取得できるが、マークダウンファイルの中身を上手く扱うには、データレイヤの中で「ファイルノード」を「MDXノード」や「マークダウンRemarkノード」の形式に変換する必要がある。(冒頭の図参照)

次回は「MDXノード」に変換してファイルの中身(ブログ記事)を表示する。

関連記事

つづき

GatsbyのデータレイヤーからgraphQLでデータを取得する

はじめに

Gatsbyでブログを書くためには、1ページずつHTMLを書くわけにもいかないので、記事を書いたデータベースからデータを取得してコンポーネントに組み込む必要がある。

Gatsbyではデータの取得にGraphQLを利用する。

GraphQLでのデータ取得方法の基礎的なやり方をメモする。

参考
システム構成図:GatsbyブログをGatsby Cloudで公開してみた - エンジニアを目指す日常ブログ
GraphQLとは:tomiko0404.hatenablog.com

この記事のゴール

  • GraphQLのクエリ作成方法と、結果の利用方法を理解する。
  • gatasby-config.jsで設定したsiteMetadataのデータをGraphQLで取得し、コンポーネントに反映する。

前回の記事

tomiko0404.hatenablog.com

前提

  • gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
  • Vagrantを開発環境として利用
  • Gatsby Cloudで公開済(今回は利用しないので、なくても可)

Gatsbyデータ管理のしくみ

以下の図のように、まず「ソースプラグイン」と呼ばれるプラグインを利用し、データを「データレイヤー」に取り込む。データレイヤーのデータにGraphQL APIでアクセスし、コンポーネント側で利用する。

ソースプラグイン

以下のサイトでgatsby-souce-で検索するとソースプラグインが出てくる。

https://www.gatsbyjs.com/pluginswww.gatsbyjs.com

  • gatsby-source-filesystem
  • gatsby-source-contentful
  • gatsby-source-graphql

等がある。例えば、ヘッドレスCMSの「Contentful」からデータを取得する場合にはgatsby-source-contentfulを使うのが良い。

ソースプラグインを利用したデータの取得方法は後日記載

siteMetadataオブジェクトの取得

gatsby-config.jsmodule.exportsしているデータは、ソースプラグインを利用しなくても自動的にGatasbyのデータレイヤにプルされている。

gatsby-config.jsで設定したsiteMetadataオブジェクトは、GatsbyデータレイヤーからGraphQL APIで取得できる状態になっているので、試しに取得してみる。

GraphQLでのデータ取得

useStaticQuery関数を利用してデータ取得

Aboutページに、siteMetadatatitleを取得して追加してみる。

情報の取得には、今回useStaticQueryを使う。

import { useStaticQuery, graphql } from 'gatsby';

でのインポートと、

    const data = useStaticQuery(graphql`
  // 要求電文
  `)

が必要である。

取得したデータは、通常のオブジェクトと同様

<p>{data.site.siteMetadata.title}</p>

のように利用できる。

src/pages/about.js

import { useStaticQuery, graphql } from 'gatsby';
import * as React from 'react'
import MyLayout from '../components/myLayout';


const About = () => {

    const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

    console.log(data)

    return (

        <MyLayout pageTitle="このサイトについて">
            <p>{data.site.siteMetadata.title}</p>
            <p>ayです。Gatsbyを利用し作成しています。</p>
            <p>お問い合わせはこちら 03-xxxx-xxxx</p>
        </MyLayout>
    )

}

export default About;

Aboutページに、data.site.siteMetadata.titleを表示することができた。

取得したtitleを&lt;p&gt;タグで表示
取得したtitleを<p>タグで表示

graphql関数を利用したデータ取得

インポート文は以下。

import { graphql } from 'gatsby';

クエリの書き方は以下のようになる。

export const query = graphql`
     //要求電文
`

このように書くと、自動的にコンポーネントprops.dataに値が渡される。

詳細は、以下の記事を参照。

GraphiQLを活用した要求電文の生成

要求電文の生成方法にくせがあるので、GraphiQLというツールを利用する。

こちらも参照:GraphQLって何? - エンジニアを目指す日常ブログ

Gatsbyでは、gatsby developを実行して、

http:localhost:8000/___graphiql

にアクセスすると、GraphiQLでGraphQLデータレイヤーにあるフィールドに対する要求電文と応答電文をテストできる。

以下のように、Gatsbyデータレイヤーで利用できる、クエリのルートフィールドが一覧になっている。

GraphiQLのquery一覧
GraphiQLのquery一覧

右側のDocで、今回使うクエリのsiteを確認すると、

  • 引数
buildTime: DateQueryOperatorInput
children: NodeFilterListInput
host: StringQueryOperatorInput
id: StringQueryOperatorInput
internal: InternalFilterInput
jsxRuntime: StringQueryOperatorInput
parent: NodeFilterInput
pathPrefix: StringQueryOperatorInput
polyfill: BooleanQueryOperatorInput
port: IntQueryOperatorInput
siteMetadata: SiteSiteMetadataFilterInput
  • 応答値の型
Site

となっている。 Site型の構成は、

buildTime(
    difference: String
    formatString: String
    fromNow: Boolean
    locale: String
): Date
siteMetadata: SiteSiteMetadata
port: Int
host: String
polyfill: Boolean
pathPrefix: String
jsxRuntime: String
id: ID!
parent: Node
children: [Node!]!
internal: Internal!

となっている。

siteMetadata: SiteSiteMetadataSiteSiteMetadata型の構成は

title: String
description: String
author: Author
siteUrl: String
social: Social

となる。

これをふまえて、siteツリーを開いていってtitleにチェックを付けると要求電文が出来上がる。

応答電文としてtitleが取得できていることがわかる。

GraphiQLを利用して要求電文の作成
GraphiQLを利用して要求電文の作成

titleには、紫色と青色が2つあるが、紫色のほうは絞り込み条件を設定する際などに使用するものなので、今回は青色にチェックを入れた。

ここで作成した要求電文には、MyQueryという名前がついているが、コンポーネント内で利用するときはその部分は削除することに注意する。

【参考】useStaticQuerygraphqlどちらを使うか

gatsby-starter-blogスターターキットのindex.jsには既にGraphQLのクエリが存在するが、そちらではuseStaticQueryを利用していない。

src/pages/index.js

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      nodes {
        excerpt
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
        }
      }
    }
  }
`

以下のサイトに解説があった。

dezanari.com

useStaticQueryを使わない場合のクエリを「ページクエリ」と呼ぶそうで、useStaticQueryコンポーネントの中に書くのに対し、ページクエリはコンポーネントの外に書く。

また、ページクエリは、単独でページになるコンポーネント/pages配下にあるようなコンポーネント)でしか使えないという制約があるらしい。

注意:graphqlのクエリは、1つのコンポーネントに1つしか入れられない

graphqlのクエリは、1つのコンポーネントに1つしか入れられないようだ。

index.jsで同じことを実施すると、エラーになってしまった。 利用しているgatsby-starter-blogスターターキットでは、index.js上に既に完成されたGraphQLのクエリが存在するため、エラーになってしまった。


 ERROR #85910  GRAPHQL

Multiple "root" queries found: "(クエリ名1)" and
"(クエリ名2)".
Only the first ("(クエリ名2)") will be registered.

Instead of:

   1 | query (クエリ名2) {
   2 |   site {
   3 |     #...
   4 |   }
   5 | }
   6 |
   7 | query (クエリ名1) {
   8 |   site {
   9 |     #...
  10 |   }
  11 | }

Do:

  1 | query
(クエリ名1)(クエリ名2) {
  2 |   site {
  3 |     #...
  4 |   }
  5 |   site {
  6 |     #...
  7 |   }
  8 | }

This can happen when you use two page/static queries in one file. Please combine those into one query.
If you're defining multiple components (each with a static query) in one file, you'll need to move each component to its own file.

おわりに

Gatsbyのデータレイヤーに存在するデータをGraphQL APIで取得することができた。

関連記事

つづき

Gatsbyブログでのプラグイン利用方法/画像の挿入方法

はじめに

Gatsbyプラグインを導入する方法をメモする。 絶対に必要になる、画像挿入のためのプラグインgatsby-plugin-imageを導入してみる。

参照したサイト:
www.gatsbyjs.com

この記事のゴール

  • Gatsbyでのプラグイン利用方法を理解する。
  • gatsby-plugin-imageを利用して静的画像を挿入する。

前回の記事

tomiko0404.hatenablog.com

パッケージの利用方法

おおまかな流れは以下。

  1. 目的のパッケージと、その依存関係にあるパッケージをnpm installする
  2. gatsby-config.jsプラグイン設定を追記する
  3. インポートして呼び出す

有名なプラグインgatsby-plugin-image

インストールの実施

インストール方法は、以下のようなプラグインの説明サイトを見て確認する。インストール用コマンドが記載されている。 www.gatsbyjs.com

依存関係のあるパッケージがあるので、同時にインストールする必要がある。 例えば、gatsby-plugin-image

を同時にインストールする必要がある。

ただし、gatsby-starter-blogスターターキットには既にプラグインが入っている(package.jsonに入っているので、gatsby newした段階でnpm installされている)ので改めて行う必要は無さそう。

以下コマンドを実施してみたところ、package.jsonの該当パッケージのバージョンが最新に更新された。

$  npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-source-filesystem

gatsby-config.jsへの追記

インストールした依存パッケージ全てを記載する必要は無いらしい。その説明も、プラグイン説明ページに記載されている。

スターターキットを利用しているので、私の環境では既に以下の記載がされていた。他のプラグインの記載もあった。

gatsby-config.js

module.exports = {
    ・・・(省略)・・・
    plugins: [
      `gatsby-plugin-image`,
      `gatsby-transformer-sharp`,
      `gatsby-plugin-sharp`,
      ・・・(省略)・・・,
    ],
  }

プラグインの利用

以下の文でインポートして、<StaticImage>コンポーネントを利用できる。

import { StaticImage } from 'gatsby-plugin-image'

index.jsでインポートした上で、以下記載を追加した。

src/pages/index.jsreturn文の中身

      {/* 画像を追加(src/images/から) */}
      <StaticImage
        alt="Gatsbyブログ勉強中の画像"
        src="../images/gatsbyブログの概要-Page-1.jpg"
      />

      {/* 画像を追加(APIから) */}
      <StaticImage
        alt="Gatsbyブログ勉強中の画像"
        src="https://source.unsplash.com/rPOmLGwai2w"
      /> 

参考:gatsby-config.jsについて

プラグインの管理や、メタデータの設定などを行なう。 ルートフォルダに置く必要がある。

参考:gatsby-config.jsの説明 Gatsby Config API | Gatsby

gatsby-config.jsはホットリロード対象にならないようなので、開発時に本ファイルを変更した際は再度gatsby developする必要があるとのこと。

gatsby-config.jsはモジュールを切り出したファイル

以下記事でまとめた通り、Node.jsでは

module.exports = () => {};

のように、module.exportsに関数や変数を代入すると、モジュールとして呼び出すこととができるようになる。

tomiko0404.hatenablog.com

gatsby-config.jsの記載は以下のようになっており、オブジェクトをモジュール化していることがわかる。

module.exports = {
  siteMetadata: {
      //省略
  },
  plugins: [
      //省略

  ],
}

このモジュールはGatsby側で自動で読み込まれる。

index.js全文

import * as React from "react"
import { Link, graphql } from "gatsby"

import Bio from "../components/bio"
import Layout from "../components/layout"
import Seo from "../components/seo"

import { StaticImage } from "gatsby-plugin-image"

const BlogIndex = ({ data, location }) => {
  const siteTitle = data.site.siteMetadata?.title || `Title`
  const posts = data.allMarkdownRemark.nodes


  if (posts.length === 0) {
    return (
      <Layout location={location} title={siteTitle}>
        <Seo title="All posts" />
        <Bio />
        <p>
          No blog posts found. Add markdown posts to "content/blog" (or the
          directory you specified for the "gatsby-source-filesystem" plugin in
          gatsby-config.js).
        </p>
      </Layout>
    )
  }

  return (
    <Layout location={location} title={siteTitle}>
      <Seo title="All posts" />
      <Bio />


      {/* Aboutページを追加*/}
      <Link to="/about">このサイトについて</Link>

      {/* 画像を追加(src/images/から) */}
      <StaticImage
        alt="Gatsbyブログ勉強中の画像"
        src="../images/gatsbyブログの概要-Page-1.jpg"
      />

      {/* 画像を追加(APIから) */}
      <StaticImage
        alt="Gatsbyブログ勉強中の画像"
        src="https://source.unsplash.com/rPOmLGwai2w"
      />      

      <ol style={{ listStyle: `none` }}>
        {posts.map(post => {
          const title = post.frontmatter.title || post.fields.slug

          return (
            <li key={post.fields.slug}>
              <article
                className="post-list-item"
                itemScope
                itemType="http://schema.org/Article"
              >
                <header>
                  <h2>
                    <Link to={post.fields.slug} itemProp="url">
                      <span itemProp="headline">{title}</span>
                    </Link>
                  </h2>
                  <small>{post.frontmatter.date}</small>
                </header>
                <section>
                  <p
                    dangerouslySetInnerHTML={{
                      __html: post.frontmatter.description || post.excerpt,
                    }}
                    itemProp="description"
                  />
                </section>
              </article>
            </li>
          )
        })}
      </ol>
    </Layout>
  )
}

export default BlogIndex

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      nodes {
        excerpt
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
        }
      }
    }
  }
`

参考:適当な画像を持ってくるサイト

Unsplashというサイトから画像を借用することができる。APIで持ってこられるので便利。

https://source.unsplash.com/{画像のID}

にアクセスすると、画像データを取得できる。

unsplash.com

使い方の説明は以下のサイトがとてもわかりやすい。

bagelee.com