Astro製のブログで読んだ本リストを管理するためにmicroCMSを使ってみた

Astro製のブログで読んだ本リストを管理するためにmicroCMSを使ってみた

目次

はじめに#

Web クリエイターボックスみたいに読んだ本リストを自分のプラットフォームで管理したいと思い、このブログ内に読んだ本リストを追加しました。サイドバーにリンクがあるので覗いてみてください!

読んだ本の管理に microCMS を使ったので、Astro 製のブログで本の管理を microCMS で行う方法をまとめます。まだ、データ数が少ないのでページネーションは実装してないのですが、実装したら追記します。

microCMS の準備#

microCMS のアカウントを作成して、コンテンツ(API)を作成し API 設定をしましょう。下記のように設定します。

APIスキーマ
APIスキーマ

読んだ本リスト API のエンドポイントは book にしました。

API スキーマの例#

本のリストなので、自分は下記のように設定しました。

フィールド ID表示名種類
titleタイトルテキストフィールド
reviewレビューリッチエディタ
urlAmazon リンクテキストフィールド
cover表紙画像テキストフィールド
author著者テキストフィールド
publishedDate発売日日時
categoryカテゴリーコンテンツ参照
postlink紹介記事テキストフィールド

環境変数の設定#

microCMS の API にアクセスするために、登録情報と API キーを環境変数に設定しましょう。
クライアントサイドで記事ページの下にも読んだ本を表示したかったので、PUBLICを先頭につけています。PUBLICを先頭に付けるとクライアントサイドでも環境変数を読み込めるようになります。

.env
PUBLIC_MICROCMS_SERVICE_DOMAIN=<YOUR_SERVICE>
PUBLIC_MICROCMS_API_KEY=<YOUR_API_KEY>

microCMS JavaScript SDK のインストール#

microCMS を Astro で使うために、microcms-js-sdk をインストールしましょう。

npm install microcms-js-sdk

読んだ本リストを作成する#

microCMS の API を使って、読んだ本リストを作成しましょう。今回は/bookページに読んだ本リストの一覧を表示するサイトを想定します。

まずは、microCMS にある読んだ本リストの全件とカテゴリーを取得するコードを書きます。src/scripts/library/microcms.tsに API 呼び出しのコードを書きます。

src/scripts/library/microcms.ts
// SDK利用準備
import type { MicroCMSListContent, MicroCMSQueries } from "microcms-js-sdk";
import { createClient } from "microcms-js-sdk";
 
// クライアントの作成
const client = createClient({
  serviceDomain: import.meta.env.PUBLIC_MICROCMS_SERVICE_DOMAIN,
  apiKey: import.meta.env.PUBLIC_MICROCMS_API_KEY,
});
 
// 型定義
export type Books = {
  title: string;
  review: string;
  url: string;
  cover: string;
  author: string;
  category: BooksCategory;
  publishedDate: Date;
  postlink: string;
} & MicroCMSListContent;
 
export type BooksCategory = {
  name: string;
  slug: string;
} & MicroCMSListContent;
 
// 本の一覧を全件取得
export const getBooks = async (queries?: MicroCMSQueries) => {
  return await client.getAllContents<Books>({ endpoint: "books", queries });
};
 
// カテゴリーの一覧を取得
export const getBooksCategory = async (queries?: MicroCMSQueries) => {
  return await client.getList<BooksCategory>({ endpoint: "category", queries });
};

client.getListメソッドもありますが、こちらは取得数に制限があるので、全件取得するclient.getAllContentsを使っています。引数にはエンドポイント(books や category)を設定して、クエリは関数の引数から渡すことができます。

詳しいクエリパラメータに関しては、公式ドキュメントを参照してください。

次に、src/pages/book/index.astroに読んだ本リストを表示するコードを書いていきましょう。

src/pages/book/index.astro
---
import MainGridLayout from "@/layouts/MainGridLayout.astro";
import { getBooks, getBooksCategory } from "@/scripts/library/microcms";
 
// 本リストとカテゴリーを取得
const books = await getBooks({ fields: ['id', 'title', 'cover', 'category']});
const categories = await getBooksCategory({ fields: ['id', 'name', 'slug']});
---
 
<MainGridLayout>
  <ul class="category__list card-base">
    {categories.contents.map((category) => (
      <li class="category__item">
        <a class="category__link btn-regular" href={`/book/category/${category.slug}`}>
          {category.name}
        </a>
      </li>
    ))}
  </ul>
  <ul class="book__list">
    {books.map((book) => (
      <li>
        <a class="book__card" href={`/book/${book.id}`}>
          <div class="book__card-img">
            <img src={book.cover} alt={book.title} />
          </div>
          <div class="book__card-title">
            {book.title}
          </div>
        </a>
      </li>
    ))}
  </ul>
</MainGridLayout>

ここでは、先程作成したgetBooksgetBooksCategoryを使って、読んだ本リストとカテゴリーを取得し map で回して表示しています。fields には取得したいデータのキーを指定します。

次に本の詳細ページとカテゴリー別のページを作成しましょう。

本の詳細ページを作成する#

本の詳細データを取得するコードを書きます。src/scripts/library/microcms.tsにコードを追加しましょう。

src/scripts/library/microcms.ts
export const getBooksDetail = async (
  contentId: string,
  queries?: MicroCMSQueries
) => {
  return await client.getListDetail<Books>({
    endpoint: "books",
    contentId,
    queries,
  });
};

getListDetailメソッドは contentId を指定して、指定した ID のデータを取得することができます。

続いて本の詳細ページ用にsrc/pages/book/[bookId].astroを作成しましょう。getBooksDetail関数を使って、本の詳細データを取得して表示します。

src/pages/book/[bookId].astro
---
import MainGridLayout from "@/layouts/MainGridLayout.astro";
import { getBooks, getBooksDetail } from "@/scripts/library/microcms";
import { Icon } from "astro-icon/components";
 
// 詳細記事ページの全パスを取得
export async function getStaticPaths() {
  const response = await getBooks({ fields: ['id'] });
  return response.map((content) => ({
    params: { bookId: content.id },
  }));
}
 
// 詳細記事ページのデータを取得
const { bookId } = Astro.params;
const book = await getBooksDetail(bookId as string);
// 日付をフォーマット
const date = new Date(book.publishedDate as Date);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
---
 
<MainGridLayout>
  <div class="book__detail card-base">
    <h1 class="book__detail-title">{book.title}</h1>
    <div class="book__detail-body">
      <div class="book__detail-right">
        <a href={book.url} class="book__detail-img" target="_blank">
          <img src={book.cover} alt={book.title} />
        </a>
        <div class="book__detail-links">
          <a href={book.url} class="book__detail-link btn-regular" target="_blank">Amazon</a>
          {book.postlink && (
            <a href={`/blog/${book.postlink}`} class="book__detail-link btn-regular">記事を見る</a>
          )}
        </div>
      </div>
      <dl class="book__detail-info">
        <div class="book__detail-info-category-wrap">
          <a href=`/book/category/${book.category.slug}` class="book__detail-info-category">
            {book.category.name}
          </a>
        </div>
        <div class="book__detail-info-item">
          <dt>著者</dt>
          <dd>{book.author}</dd>
        </div>
        <div class="book__detail-info-item">
          <dt>発売日</dt>
          <dd>{`${year}${month}${day}日`}</dd>
        </div>
        <div class="book__detail-info-body" set:html={book.review}></div>
      </dl>
    </div>
    <div class="book__detail-bottom-link-wrap">
      <a href="/book" class="book__detail-bottom-link">
        <span class="link__icon">
          <Icon name="material-symbols:chevron-left-rounded" />
        </span>
        <span class="book__detail-bottom-link-text">読んだ本リストに戻る</span>
      </a>
    </div>
  </div>
</MainGridLayout>

getStaticPaths関数は動的なパスがどういう値をとるかを定義します。ここでは、getBooks関数で取得した ID をパスとして設定しています。

book.reviewはリッチエディタで入力したデータなので、改行などを正しくレンダリングするためset:htmlを使って HTML をそのまま表示します。

これで本の詳細ページができました。

カテゴリーページを作成する#

カテゴリー別のページを作成しましょう。src/pages/book/category/[slug]/index.astroを作成します。

src/pages/book/category/[slug]/index.astro
---
import MainGridLayout from "@/layouts/MainGridLayout.astro";
import { getBooks, getBooksCategory } from "@/scripts/library/microcms";
 
// カテゴリー別のパスを取得
export async function getStaticPaths() {
  const response = await getBooks({ fields: ['id', 'category'] });
 
  return response.map((content) => ({
    params: { slug: content.category.slug },
    props: {
      categoryId: content.category.id,
      slug: content.category.slug,
      name: content.category.name,
    },
  }));
}
 
const { categoryId, slug, name } = Astro.props;
 
// カテゴリー別の本のリストを取得
const books = await getBooks({
  fields: ['id', 'title', 'cover', 'category'],
  filters: `category[equals]${categoryId}`
});
 
const categories = await getBooksCategory({ fields: ['id', 'name', 'slug']});
---
 
<MainGridLayout>
  <ul class="category__list card-base">
    {categories.contents.map((category) => (
      <li class="category__item">
        <a class="category__link btn-regular" href={`/book/category/${category.slug}`}>
          {category.name}
        </a>
      </li>
    ))}
  </ul>
  <ul class="book__list">
    {books.map((book) => (
      <li>
        <a class="book__card" href={`/book/${book.id}`}>
          <div class="book__card-img">
            <img src={book.cover} alt={book.title} />
          </div>
          <div class="book__card-title">
            {book.title}
          </div>
        </a>
      </li>
    ))}
  </ul>
</MainGridLayout>

ここでもgetStaticPaths関数を使って、カテゴリー別のパスを取得しています。getBooksの引数にfiltersをでカテゴリー ID を指定することで、そのカテゴリーの本のリストを取得できます。

category[equals]${categoryId}のように[equals]を使うことで、id と一致する本のデータを取得できます。

以上で、本の一覧・詳細・カテゴリー別のページができました!
最後におまけでブログ記事の下に PR として本のリストをランダムで 4 件表示する方法を紹介します。

おまけ: ブログ記事の下に本のリストをランダムで 4 件表示する#

新しくコンポーネントとしてPostBookArea.astroを作成しました。このコンポーネントではクライアントサイドで JavaScript を使って、microCMS の API からデータを取得しています。

このブログは SSG として公開しているので、Astro のコードフェンスの中で取得すると、ビルド時にデータを取得するので固定になってしまいます。なので、クライアントサイドで取得するように<script>の中で microCMS の API からデータを取得するコードを書いていきます。

src/components/PostBookArea.astro
<div class="post-book-area card-base">
  <h2>PR</h2>
 
  <ul class="post-book-area__list"></ul>
</h2>
 
<script>
  import { getBooks } from "@/scripts/library/microcms";
 
  const allBooks = await getBooks({ fields: ['title', 'cover', 'url'] });
  const randomBooks = allBooks.sort(() => Math.random() - 0.5).slice(0, 4);
  const bookList = document.querySelector(".post-book-area__list");
 
 
  if (randomBooks && bookList) {
    bookList.innerHTML = randomBooks.map((book) => `
      <li class="post-book-area__item">
        <a class="post-book-area__link" href="${book.url}" target="_blank">
          <div class="post-book-area__img">
            <img src="${book.cover}" alt="${book.title}" />
          </div>
          <div class="post-book-area__title">${book.title}</div>
        </a>
      </li>
    `).join('');
  }
</script>

ここでは microCMS の API から取得したデータをランダムに 4 件取得して表示しています。
このコンポーネントをブログ記事の詳細ページに追加します。

src/pages/blog/[slug]/index.astro
---
import PostBookArea from "@/components/PostBookArea.astro";
---
 
<MainGridLayout>
  <div class="article card-base">
    <MarkdownBody>
      <Content components={{ img: Figure }} />
    </MarkdownBody>
 
  <div class="post-book-area">
    <PostBookArea />
  </div>
 
</MainGridLayout>

これでブログ記事の下に本のリストをランダムで 4 件表示することができました。

まとめ#

Astro 製のブログに microCMS で管理した読んだ本リストを表示する方法を紹介しました。読書メーターも使ってましたが、自分のプラットフォームで読んだ本を管理することができるようになったので、気軽に読んだ感想を残せるようになってよかったです!

サイドバーにリンクがあるのと、このページにあるので覗いてみてください!

参考#

Related Post
ブログのデザインを大幅に変更しました
image

はじめに

このブログのデザインを大幅に変更しました。

変更後のTopページ

変更した経緯としては、2つあります。 以前のブログのデザインは、Astroのfuwariテーマを参考にして作成していたのですが、fuwariベースの派生テーマが公式の方でも増えてきており、ブログのデザインが他の人と被るのが嫌だなとなり思い切ってデザインを大幅に変更してみました。

また、以前は記事にカテゴリーを設定していたのですが、この雑記ブログにはタグのみの方が記事の分類として迷いがなくなって書きやすくなると感じたので、カテゴリーを廃止して一覧性をよくしようと思いました。

この記事では、デザインを変更したAstro製のこのブログについて、サムネイルに記事の一部を表示する方法を紹介します。

Meta Data
公開日:2026-02-06
Tags:
#ブログ
#Astro
Astroで作ったブログで更新日時を自動で取得する方法

はじめに

このブログのデザインを大幅に変更したいのと、記事の一覧を更新順で表示したいと思ってたのですが、更新日の取得方法に悩んでいました。

Astro の公式のチュートリアルでは更新日は記事のフロントマターに設定する方法が紹介されていましたが、毎回手動で設定するのは面倒です。

そこで自動で取得する方法を考えた結果、Git のコミット日時を取得してその日付を更新日として表示する方法を実装できたので紹介します。

Git のコミット日時を取得する

それでは、Git のコミット日時を取得する方法について説明します。 Git 関連の情報はgit logコマンドで取得できます。 次のようにコマンドを打ってみましょう。

# 指定したファイルの最新のコミット日時を取得する
## -1 : 最新の1件のみ取得
## %ai : 日付
## filePath : 調べたいファイル
git log -1 --format=%ai -- filePath

オプションについてはコメントの通りになります。

Meta Data
公開日:2026-01-25
更新日:2026-02-04
Tags:
#Astro
技術ブログにブラウザの対応状況を表示するBaseline Status Webコンポーネントを入れる方法

はじめに

技術ブログを複数運営しているのですが、CSS などのプロパティで一部のブラウザのみでしか対応していないものがあります。その際に、ブラウザの対応状況を表示するコンポーネントを入れておくと、ブラウザの対応状況が分かるので、ブログを見ている人にも分かりやすいと思います。

↓ 運営している技術ブログ

https://hypb.dev

https://feylo.dev

今回紹介する Baseline Status Web コンポーネントは、ブラウザの対応状況を表示するための Web コンポーネントです。このコンポーネントをブログ内で使用することで、ブラウザの対応状況を簡単に表示することができます。この Web コンポーネントを Astro 製のブログと Nuxt 製のブログで使う方法を紹介します。

使用イメージは ↓ のような感じになります。

<BaseLineStatus featureId="anchor-positioning" />

Baseline Status Web コンポーネントの使い方

npm で使う場合

npm でbaseline-statusをインストールします。

npm install baseline-status

使用する際はそのまま import して表示する featureId を指定するだけで上記のような感じで表示されます。

import "baseline-status";
<baseline-status featureId="anchor-positioning" />
Meta Data
公開日:2025-07-16
Tags:
#ブログ
#Astro
Astro製のブログにCloudinaryを導入する
image

はじめに

このブログと技術ブログは、Astro の Content Collection を使ってマークダウンでブログを書いていて、記事は GitHub で管理してます。

Astro の Image コンポーネントを使うために、ブログ記事の画像も src/assets/images/配下に配置していました。しかし、ローカルで画像を管理したくないなと思ったので Cloudinary を導入してみました。

Astro での Cloudinary の導入方法について、この記事では紹介します。 Cloudinary の詳しいオプションなどは解説しないのと、Cloudinary のアカウントが既にあるのを前提に説明します。

まだの方は下記 URL からアカウントを作成してください。

https://cloudinary.com/

Astro Cloudinary のインストール

Astro で使える Cloudinary のコンポーネントが用意されてます。下記コマンドで Astro Cloudinary をインストールしましょう。

npm install astro-cloudinary

インストールできたら、プロジェクトのルートに.envファイルを作成して、Cloudinary のアカウント情報を記述します。

PUBLIC_CLOUDINARY_CLOUD_NAME="<Your Cloud Name>"
Meta Data
公開日:2025-05-19
Tags:
#Web
#Astro
Astroで作ったサイトをGitHub Pagesにデプロイする
image

はじめに

Astro で作ったサイトを GitHub Pages にデプロイする方法について紹介します。 GitHub Pages を使うことで簡単に作ったサイトを公開することができます。

基本的には公式サイトのドキュメント通りにやればできるでしょう。 ここではカスタムドメインを使わないリポジトリ名が後ろにつくデプロイ方法を紹介します。

https://docs.astro.build/ja/guides/deploy/github/

GitHub Pages 用に Astro の設定をする

astro.config.mjssitebaseオプションの設定を追加します。

import { defineConfig } from "astro/config";

export default defineConfig({
  site: "https://<username>.github.io",
  base: "<your-repo-name>",
});

ここでsiteの username にはご自身の GitHub のユーザー名を、baseの your-repo-name にはデプロイするリポジトリ名入れてください。

Meta Data
公開日:2025-04-27
Tags:
#Astro
#GitHub
Astroで雑に書ける個人ブログを作りました
image

雑に書ける場所が欲しくなったので、Astro で 2 個目の個人ブログを作成しました。 普段は、👇 の技術ブログで主にフロントエンドに関しての記事を書いています。

https://hypb.dev

上記のブログでは、技術系しか書けない感じになっていたので、こちらではもっと雑にインターネット上に残していきたいと思います。

Aboutページに書いてある通り、音楽・映画・小説などが好きなので、それらに関する記事を書いていきたいです。

デザインについて

デザインに関しては、Astro テーマのFuwariを参考にして作成しました。 ほぼ丸パクリですが、Fuwari テーマを見て雑記ブログを作りたいと思ったのでこれで良しとします。

フォントは日本語はKiwi Maru、英語はIBM Plex Monoを使っています。フォントのおかげで可愛い感じが出て良かったです ✨

Fuwari テーマの方は、Svelte や swup を使ってたりでカスタマイズするのが難しそうな感じですが、自分の作成した本ブログはコードも分かりやすく作成したので、サクサク作成することができました!

Meta Data
公開日:2025-04-01
Tags:
#ブログ
#Astro
Info