🐎

Astro × microCMS × Vercelで構築するモダンブログの作り方 完全ガイド

2023/05/09に公開
2

はじめに

エンジニアになってから、個人ブログでさまざまな記事を書いてきましたが、そろそろリプレイスしたいと考え、Astroでシンプルなブログサイトを作成してみました。その作成手順について記事にしていきます。データ管理はmicroCMSを使用し、サイトのホスティングはVercelで構築しています。



作成したもの

作成した記事を一覧で表示するシンプルなデザインにしました。


Github

作成したサンプルデータは下記リポジトリに格納しています。

https://github.com/MASAKi-cell/astro-myBlog


バージョン情報

今回使用したNodeと主なパッケージのバージョン情報です。

  • Node.js - v18.14.2
  • Astro - v2.3.0
  • microcms-js-sdk - v2.3.3



技術概要

Astro

https://docs.astro.build/ja/getting-started/

Astroは静的サイト生成ツールとして使用されるフレームワークです。
最大の特徴は、サーバーファーストであることです。JavaScriptを可能な限りサーバーで処理し、クライアントには静的なHTMLだけを送信しようとします。ビルド時にクライアントサイドのJavaScriptをできるだけ取り除くことで、サイトに表示するための必要なファイル量をできるだけ減らしています。そのため、ページの読み込みからユーザー操作時において高速にWebサイトを読み込むことが可能になります。


microCMS

https://microcms.io/

microCMSはヘッドレスSMC(コンテンツ管理システム)のことで、ウェブサイトを表示するための情報の管理を行うことができるサービスです。

ヘッドレスについて

ヘッドレス(headless)は表示部分を持たないという意味で、単純にコンテンツのデータ管理に特化しています。ページ自体は開発者が実装し、APIスキームの定義やデータの構造などはヘッドレスCMSで作成して、APIを利用してそれらの内容を取得することができます。

一方で、WordPressやMovableTypeのような、データ入力画面の作成、データ管理、画面表示など全ての機能が揃ったシステムが存在しますが、この場合すべてがそのシステムの管理下に置かれます。全ての機能が揃っているため、要件とマッチしていれば工数の削減が可能ですが、案件によっては機能を満たしていないこともあます。そのような場合は、データ管理はヘッドレスCMSを利用し、実装部分を要件に合わせて開発していくことが可能になります。


Vercel

https://vercel.com/

Vercelとは、Next.jsを開発したVercel Inc.が提供しているホスティングサービスです。gitリポジトリさえあれば、Vercelがコードを読み込み、設定なしで簡単にデプロイすることができます。



Astroのプロジェクト立ち上げ

まずは以下のコマンドラインを実行して、Astroを立ち上げます。

npm create astro@latest

プロジェクトのディレクトリに移動してnpm run devコマンドを実行すると、開発サーバーが立ち上がります。

cd astro-myBlog
npm run dev

> purple-proxima@0.0.1 dev
> astro dev

  🚀  astro  v2.3.0 started in 34ms
  
  ┃ Local    http://127.0.0.1:3000/
  ┃ Network  use --host to expose

http://127.0.0.1:3000/にアクセスして以下の画面が表示されていればプロジェクトの立ち上げ成功です。



microCMSのセットアップ

astroの実装に入る前に、microCMSと連携して作成したブログデータを取得するためにmicroCMSのアカウントを作成します。

https://app.microcms.io/signup


microCMSのアカウントを作成したら、今回利用するサービス名とサービスIDを任意のものに設定します。


API基本の情報を入力します。APIエンドポイントは実際にデータを取得、更新する際に必要になります。


APIスキーマの作成

APIから取得するスキーマを定義して、作成します。タイトル、メイン画像と記事の中身を格納するコンテンツを設定します。


コンテンツの追加

コンテンツを追加してブログを作成します。


コンテンツが表示されていれば成功です。



microCMS JavaScript SDKをインストール

https://www.npmjs.com/package/microcms-js-sdk

microCMS JavaScript SDKをインストールします。
microCMS JavaScript SDKはmicroCMSのAPIを簡単に利用できるようにするためのJavaScriptライブラリです。ブラウザ環境とNode.js環境の両方で利用可能です。

以下のコマンドを実行して、AstroプロジェクトにmicroCMS JavaScript SDKをインストールします。

npm install microcms-js-sdk


.env
SERVICE_DOMAIN='YOUR_DOMAIN' // microCMSのドメイン名
API_KEY='YOUR_API_KEY' // microCMSのAPI Key
ENDPOINT='YOUR_ENDPOINT' // microCMSのエンドポイント

microCMSのドメイン名とAPI Keyそれからエンドポイントを.envファイルに追加します。


microcms.ts
import { createClient } from "microcms-js-sdk";

export const client = createClient({
  serviceDomain: import.meta.env.SERVICE_DOMAIN,
  apiKey: import.meta.env.API_KEY,
});

公式ドキュメントに従ってSDKをインポートし、microCMSクライアントを作成します。import.meta.envで.envファイルのドメイン名やAPI Keyにアクセスすることが可能です。これで、microCMSで作成したブログの中身の取得や更新ができるようになりました。



データ取得

https://document.microcms.io/content-api/get-list-contents

src/repositories/microcmsRepository.ts
import { microcmsClient } from "../api/microcms";
import type { MicroCMSQueries } from "microcms-js-sdk";

export const getAllBlogs = async (queries: MicroCMSQueries): Promise<any> => {
  return await microcmsClient
    .get({
      endpoint: "myblog",
      queries,
    })
    .then((res) => {
      console.log(res);
      return res;
    })
    .catch((err) => console.error(err));
};

microcmsRepositoryフォルダ内に、データを取得するメソッドを集約します。endpointはmicroCMSで設定したエンドポイントを記載し、queriesは取得件数の絞り込みやコンテンツの中で取得する要素を指定することができます。


{
  contents: [
    {
      id: 'v5smscmimy',
      title: '【Vue.js】リアクティブシステムの基礎',
      createdAt: '2023-05-01T12:17:31.791Z',
      url: [Object]
    },
    {
      id: '5dbgueei73',
      title: '配列やオブジェクト操作におけるTips',
      createdAt: '2023-04-27T12:29:07.088Z',
      url: [Object]
    },
    {
      id: 'wn6apxr2ya79',
      title: '初投稿',
      createdAt: '2023-04-27T12:28:11.993Z',
      url: [Object]
    }
  ],
  totalCount: 3,
  offset: 0,
  limit: 10
} <<< response all data

APIから正常にレスポンスされれば、コンソールに記事のタイトルや表示画像、作成された日時などの情報が表示されます。


microcmsTypes.ts
export type BlogsData = {
  contents: [
    {
      id: string;
      title: string;
      createdAt: string;
      url: BlogUrl;
    }
  ];
  totalCount: number;
  offset: number;
  limit: number;
};

export type BlogUrl = {
  url: string;
  height: number;
  width: number;
};

microcmsTypesに型情報を集約して、取得するデータを定義しておきます。


index.astro
---
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";

/** api */
import { getAllBlogs } from "../repositories/microcmsRepository";

const res = await getAllBlogs({ fields: ["id", "title", "createdAt", "url"] });
---

<Layout title="My Origin Blog">
  <!-- header -->
  <header class="header">
    <div class="header-parent">
      <div class="text-title">
        <h1 class="text-gradient">My Blog</h1>
      </div>
      <div class="row">
        <nav class="header__nav">
          <ul>
            <li class="current">
              <a class="smoothscroll" href="">Home</a>
            </li>
            <li><a class="smoothscroll" href="">Profile</a></li>
            <li><a class="smoothscroll" href="">About me</a></li>
            <li><a class="smoothscroll" href="">Contact</a></li>
          </ul>
        </nav>
      </div>
    </div>
  </header>
  <main class="main">
    <div class="content">
      {
        res.contents.map((content: any) => {
          return (
            <Card
              href={content.id}
              title={content.title}
              url={content.url.url}
              createdAt={content.createdAt}
            />
          );
        })
      }
    </div>
  </main>
</Layout>

getAllBlogsのfieldsは取得したいデータ(ここではid、title、createdAt、url)を指定して、レスポンスデータをmap関数を使用して、コンテンツの中身をそれぞれViewに反映させることでTOP画面が完成します。



個別画面の実装

microcmsRepository.ts
// Get list API detail data
export const getBlogsDetail = async (
  contentId: string,
  queries?: MicroCMSQueries
): Promise<BlogsDetail> => {
  return await microcmsClient
    .getListDetail({
      endpoint: import.meta.env.ENDPOINT,
      contentId,
      queries,
    })
    .then((res) => {
      console.log(res, "<<< response detail data");
      return res;
    })
    .catch((err) => console.error(err));
};

次に記事をクリックした時の、個別記事の表示の実装を行います。
microcmsClientのgetListDetailメソッドを使用して、記事の詳細情報を取得するAPIを作成します。


microcmsTypes.ts
export type BlogsDetail = {
  id: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
  title: string;
  content: string;
};

取得するデータの型を定義します。


src/pages/[blogId].astro
---
import Layout from "../layouts/Layout.astro";

/** types */
import type { BlogsDetail } from "../types/microcmsTypes";

/** api */
import {
  getAllBlogs,
  getBlogsDetail,
} from "../repositories/microcmsRepository";

/** utils */
import { formatDate } from "../utils/formatDate";

const { blogId } = Astro.params;
let blogData: BlogsDetail | null = null;

if (blogId) {
  blogData = await getBlogsDetail(blogId);
}
---

<Layout title="blog">
  <main>
    <h1>{blogData ? blogData.title : ""}</h1>
    <p>
      投稿日:{
        blogData ? new formatDate(blogData.revisedAt).toJpDateWithWeek() : ""
      }
    </p>
    <div set:html={blogData ? blogData.content : ""} />
  </main>
</Layout>

次にpages配下に[blogId].astroフォルダを作成します。ファイル名を[ブラケット]記法を用いて囲むことで動的ルーティングパラメーターを指定して、指定した条件に合致するルーティングを作成することができます。個々の記事をクリックすると、http://127.0.0.1:3000/レスポンスidのようにルーティングされます。getBlogsDetailのfieldsは記事を識別するidを指定します。



getStaticPaths

このままだと個々の記事をクリックした場合、以下のエラーが表示され、記事本体の文章が表示されません。

Astroで動的ルーティングパラメーターを作成する場合は、getStaticPathsメソッドを使用する必要があります。

all routes must be determined at build time, a dynamic route must export a getStaticPaths() that returns an array of objects with a params property. Each of these objects will generate a corresponding route.
Astro Doc:https://docs.astro.build/en/core-concepts/routing/#dynamic-routes

getStaticPathsメソッドは動的ルーティングを使用しているページで、ビルド時に静的生成が必要なパスを指定するために使用される関数です。


src/pages/[blogId].astro
export async function getStaticPaths() {
  const res = await getAllBlogs({ fields: ["id"] });
  return res.contents.map((content) => {
    return {
      params: {
        blogId: content.id,
      },
    };
  });
}

[blogId].astroフォルダ内にgetAllBlogsメソッドで取得した、idをgetStaticPathsのparamsにあてることで、動的ルーティングの生成が可能となります。



日付の整形

utils/formatDate.ts
 export class formatDate {
  _date: Date;
  constructor(date: Date | string) {
    if (date instanceof Date) this._date = date;
    else if (typeof date === "string") this._date = new Date(date);
    else this._date = new Date(date);
  }

  /**
   * yyyy年mm月dd日に整形する
   * @returns yyyy年mm月dd日
   */
  toJpDateString = () => {
    return `${this._date.getFullYear()}${
      this._date.getMonth() + 1
    }${this._date.getDate()}`;
  };

  /**
   * yyyy年mm月dd日(曜日)に整形する
   * @returns yyyy年mm月dd日(曜日)
   */
  toJpDateWithWeek = () => {
    const week = ["日", "月", "火", "水", "木", "金", "土"];
    const dayOfWeek = week[this._date.getDay()];
    if (this._date === undefined || dayOfWeek === undefined) return "ー";
    return `${this.toJpDateString()}${dayOfWeek}`;
  };
}

リクエストのcreatedAtパラーメータで記事の作成日時を表示させることができますが、そのまま表示させるとUTCやミリ秒まで表示される為、日付を整形するユーティリティを作成しておきます。


    <span class="update-date"
      >{new formatDate(createdAt).toJpDateWithWeek()}</span
    >

個々のコンポーネントでクラスのインスタンスを生成することで、年月日(曜日)で表示されるようになります。



Vercel

最後にVercelにログインして、作成したWebサイトをホスティングを行います。

https://vercel.com/


ログインが完了したら、公開しているGitHubのリポジトリとVercelを接続します。


接続したいGithubのリポジトリにVercelをインストールして、ビルドの設定を行います。



最後に、プロジェクト名・フレームワーク・環境変数などを設定して「Deploy」を押します。


「Congratulations!」と表示されていれば、デプロイ完了です。



さいごに

今回は、Astro×microCMS×Vercelでブログサイトを作成してみました。 どれも初めて触れる技術でしたが、1日で実装と構築を行うことができました。もし何か間違いがあれば、ご指摘いただけると助かります。




文献

https://www.codegrid.net/articles/2022-astro-1/

https://www.codegrid.net/articles/2021-microcms-1/

Arsaga Developers Blog

Discussion

RUNFUNRUNRUNFUNRUN

.envにENDPOINTの設定をする旨を追加していただくと、自分のような初学者の方にもわかりやすいと思います。
とても参考になりました。

まさきちまさきち

コメントありがとうございます。
.envファイルを設定する旨、項目追加しておきます