はきくま!制作日記:microCMS 編②
- 投稿日
microCMS の API をイラストと日記に広げたときの記録。
その1:イラスト用の API(illusts)
無料プランは API が5つまで。作品・各話・カップリングの3つに続く4つ目で、残りは1つ(日記用に確保)。
| API | 主なフィールド |
|---|---|
illusts(イラスト) |
image(画像), caption(テキスト・任意), tags(セレクト・複数・任意), novel(参照: novels・任意) |
- 1コンテンツに画像1枚。グリッド表示にするので、複数枚の投稿にはしない。漫画は投稿しない前提
titleは持たせない。フィールドIDは最初illustだったがimageに改名した。型と表示側の名前がずれると、illust.image.urlが読めずにエラーになる- R18 のフィールドは作らない(イラストに R18 は出さない想定)
- 作品との紐づけは参照(任意)。作品に紐づかないものは、絞り込みで「その他」になる
// src/lib/types.ts
export type Illust = {
id: string;
image: { url: string; width: number; height: number };
caption?: string;
tags?: string[];
novel?: Pick<Novel, "id" | "title" | "sort">;
publishedAt: string;
updatedAt: string;
};
取得は、getAllContents で全件を取り、Promise をモジュールに保持して、ビルド中の呼び出しを1回にまとめた。
// src/lib/getIllusts.ts
let cache: Promise<Illust[]> | null = null;
export function getIllusts() {
cache ??= client.getAllContents<Illust>({
endpoint: "illusts",
queries: { orders: "-publishedAt" },
});
return cache;
}
// ほかに getIllustTags(タグの一覧)、getIllustNovels(sort 順の作品一覧)、ILLUSTS_PER_PAGE = 24
並びは publishedAt の降順。updatedAt にすると、キャプションを直しただけで先頭に来てしまう。
その2:絞り込みは JS でやる
「作品」と「タグ」で絞り込み、ページ送りもしたい。静的に作ると、作品×タグの組み合わせごとにページが必要になる。タグは複数選べるので現実的ではなく、JS で絞り込むことにした。
- 全件を
liとして出力し、data-tags(JSON)とdata-novelを持たせる。25件目以降はhidden - 状態は
{ novel, tags, page }。作品は1つ、タグは複数選べて OR 検索(AND にすると絞れすぎる) - 作品を選び直したら、タグは全解除に戻す
- 状態は
?novel=…&tag=…&tag=…&page=Nとしてhistory.replaceStateで URL に反映する。直リンクや更新でも状態が戻る - 作品に紐づかないイラストが1枚でもあれば、「その他」を出す(
data-value="other"、data-novelが空のもの)
その3:novels に thumbnail
novels に画像フィールド thumbnail(任意)を追加した。ホームの小説カード用。
thumbnail?: { url: string; width: number; height: number };
- 任意にしてあるので、表示側は有無を確認してから使う
- microCMS で画像を入れて公開し直さないと、API には出てこない。出ない時は
npm run devを再起動する
気になる点(未対応):画像の配信元
ギャラリーのサムネイルとホームの画像は、https://images.microcms-assets.io/…?w=480&fm=webp のように、microCMS の URL をそのまま <img> に書いている。この形だと、HTML は静的に生成していても、画像だけは閲覧のたびに microCMS から配信され、無料プランの月20GBの転送量に数える。
イラストが増えるとここが効いてくるので、増えてきたら astro:assets の getImage でビルド時に取り込む形に寄せるか検討する。まだ何も変えていない。
その4:日記用の API(diaries)
5つ目の API。無料プランの枠はこれで使い切った(novels / chapters / couplings / illusts / diaries)。
| API | 主なフィールド |
|---|---|
diaries(日記) |
title, body(テキストエリア), tags(セレクト・複数・任意), byAi(真偽値) |
- エンドポイントは他に合わせて複数形の
diaries。管理画面のエンドポイント欄と1文字でも違うと 404 になる。表示名ではなく、英数字のエンドポイント欄の値を使う - 日付専用のフィールドは作らない。
publishedAt(投稿日)とupdatedAt(更新日)が自動で付く - 本文は、小説(テキストエリア+独自記法)や設定資料(リッチエディタ)とは別に、日記はマークダウンで持つ
// src/lib/types.ts
export type Diary = {
id: string;
title: string;
body: string;
tags?: string[];
byAi: boolean;
publishedAt: string;
updatedAt: string;
};
取得は getIllusts と同じ形で、Promise を保持して1回にまとめた。
タグ未設定の日記は「未分類」にする
タグを管理画面で選び忘れても、必ず何かのタグが付くようにしたい。管理画面側に「未選択ならこの値」というデフォルト値の設定はないので、取得の一か所で補う。
// src/lib/getDiaries.ts
export const UNCATEGORIZED = "未分類";
export function getDiaries() {
cache ??= client
.getAllContents<Diary>({ endpoint: "diaries", queries: { orders: "-publishedAt" } })
.then((list) =>
list.map((d) => ({ ...d, tags: d.tags?.length ? d.tags : [UNCATEGORIZED] })),
);
return cache;
}
d.tags ?? [...]だと空配列[]を見逃す。?.lengthで、未設定と空の両方を拾う- 表示側(一覧・詳細・タグ集計)は何も意識しなくてよい。管理画面のデータは変わらず、ビルド時に補うだけ
- 関連記事を作るときは、全員が持つ「未分類」で結びつかないよう、
t !== UNCATEGORIZEDで除外する
取得コードの読み方(メモ)
endpointは、管理画面で API を作る時に決めた名前。URL はhttps://サービスID.microcms.io/api/v1/エンドポイントgetAllContentsは、全件を100件ずつ繰り返し取ってくれるorders: "-publishedAt"の先頭の-が降順cache ??=は、空の時だけ右辺を実行して代入する。何度呼んでも API を叩くのは最初の1回
この記事の学び
- 画像1枚=1コンテンツにしておくと、グリッド・絞り込み・ライトボックスのどれにも素直に載る
- 絞り込みの組み合わせが増えるなら、静的に作らず、全件を出して JS で隠す
- フィールドIDを変えたら、型・取得・表示の3か所を同時に直す
- 補完や整形は、取得の一か所に寄せる。使う側に
?? ...を散らさない - 管理画面の内容を変えても、開発サーバーは
cacheを持っているので、反映されない時は再起動する - 本文の持ち方は用途で決める。小説は原稿の行き来のためにテキストエリア、設定資料は画像と書式のためにリッチエディタ、日記は書きやすさ重視でマークダウン