概要
- CMSにはmicroCMSを使用します
- ブログ記事を取得し、SvelteKitでSGして公開します
- デプロイ先にはCloudflare Pagesを使用します
お目当ての記事ではなさそうだった場合、ねこだけでもみていってください。
うちのねこ
https://www.instagram.com/hokke_to_unagi/
自己紹介
Web制作会社で
- 社内向けのWebアプリ開発
- エンジニア部署周りのサポート
- サイト制作の手伝い
- マネジメント
などをしたりしています。
ここ1~2年のお気に入りはSvelteです。
よろしくお願いします。
前回書いた記事
1年ほど前にSvelteKitでブログを作る記事を書きました。
あれからしばらくしてルーティング周りが変わったりしています。
また、前回はデプロイまでやっていなかったので今回はそこもやってみようと思います。
SvelteKitはまだバージョン 1.0に到達していません。
本記事の情報は古くなっている可能性があるのでご注意ください。
microCMSで記事を作成する
この辺
から登録していってください。すでにある場合は不要です。
あとでサービスIDを環境変数のVITE_SERVICE_DOMAINに登録するのでメモしておいて下さい。
忘れた人用
https://hoge.microcms.io/
のhogeの部分です。
リスト形式でAPIを作成し、フィールドは
- title(テキストフィールド)
- content(リッチエディタ)
で作成しておいてください。
APIが作れてフィールドも作成し終わったらテスト用に適当に記事をいくつか投稿しておいてください。
APIプレビューをクリックするとX-MICROCMS-API-KEYが画面に表示されているかと思うのでこちらも控えておいてください。
後ほど環境変数のVITE_API_KEYに登録します。
実装!
package.json
{
"name": "test-cloudflare-pages-sveltekit",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-cloudflare": "^1.0.0-next.31",
"@sveltejs/kit": "next",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.44.0",
"svelte-check": "^2.7.1",
"svelte-preprocess": "^4.10.6",
"tslib": "^2.3.1",
"typescript": "^4.7.4",
"vite": "^3.0.4"
},
"type": "module",
"dependencies": {
"microcms-js-sdk": "^2.2.1"
}
}
- @sveltejs/adapter-cloudflare
- microcms-js-sdk
の2点を追加しています。
npm install
npm run dev
env
さきほど2点メモしてもらいました。
.envを作成し、それぞれ追記します。
VITE_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
VITE_SERVICE_DOMAIN=hoge
svelte.config.js
adapterを@sveltejs/adapter-cloudflareに入れ替えます。
SGしたい場合も@sveltejs/adapter-staticにする必要はなく、@sveltejs/adapter-cloudflareで可能です。
import adapter from '@sveltejs/adapter-cloudflare';
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter(),
prerender: {
default: true
},
alias: {
$assets: 'src/assets',
$components: 'src/components'
}
}
};
export default config;
prerender
SGするための設定項目です。
各ページでexport const prerender = true;
を設定してもOKですが、ブログなら前ページSGするかと思うのでここで一括指定します。
vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig(() => {
return {
server: {
host: true,
port: 3000
},
plugins: [sveltekit()]
};
});
server
こだわりがあるわけではないですが、localhost:3000でみたいので設定します。
routesディレクトリ
今までindex.svelte
などのファイル名でしたが、+page.svelte
などに変わっています。
TOPページは今回どうでもいいのでとりあえず以下で作ります。
<svelte:head>
<title>Top Page</title>
</svelte:head>
<h1>Top Page</h1>
<a href="/posts" sveltekit:prefetch>Posts</a>
microcms-js-sdk
そろそろ記事の取得周りを行いたいので、microcms-js-sdkの設定をやっていきます。
以下のファイルを作成します。
import { createClient } from 'microcms-js-sdk';
export const client = createClient({
serviceDomain: import.meta.env.VITE_SERVICE_DOMAIN,
apiKey: import.meta.env.VITE_API_KEY
});
先ほど.env
に設定した環境変数をこちらで読み込みます。
Cloudflare Pagesにデプロイ時(ビルド時)は管理画面で設定する環境変数が読み込まれます。のちに設定します。
クライアントが作成できたので、いよいよ記事一覧と詳細を作っていきます。
記事一覧
routesディレクトリに以下のファイルを作成します。
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<svelte:head>
<title>Posts</title>
</svelte:head>
<h1>Posts</h1>
{#if data.contents}
<ul>
{#each data.contents as content}
<li>
<a href="/posts/{content.id}/" sveltekit:prefetch>
{content.title}
</a>
</li>
{/each}
</ul>
{/if}
export let data
の部分ですが、固定のようです。
同階層のディレクトリの+page.server.tsからデータを受け取ります。
import { error } from '@sveltejs/kit';
import { client } from '../../libs/client';
import type { MicroCMSListResponse } from 'microcms-js-sdk';
type Post = {
id: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
revisedAt: string;
title: string;
content: string;
};
/** @type {import('@sveltejs/kit').RequestHandler} */
export async function load() {
const res = await client.get<MicroCMSListResponse<Post>>({
endpoint: 'bar'
});
if (res) {
return { ...res };
}
throw error(404, 'Not found');
}
endpointのところは自分で作成したものに変更ください。
以下のbarの部分です。
https://hoge.microcms.io/api/v1/bar
記事一覧を取得した場合レスポンスは以下のようになっています。
{
"contents": [
{
"id": "idididid",
"createdAt": "2021-09-07T07:24:44.764Z",
"updatedAt": "2022-01-10T09:28:24.615Z",
"publishedAt": "2021-09-07T07:24:44.764Z",
"revisedAt": "2022-01-10T09:28:24.615Z",
"title": "仮記事",
"content": "<p><strong>本文</strong></p>"
}
],
"totalCount": 1,
"offset": 0,
"limit": 10
}
resにこちらが入ってくるので、load関数の中で展開してreturnします。
ここでreturnしたものが先ほどのexport let data
のdataに入ってきます。
なので+page.svelte
のテンプレート部分で{#each data.contents}
のようにして表示できていたんですね。
microCMSの一覧取得時、他にもtotalCount、offset、limitが返ってくるのでそちらでページネーションの実装も可能です。
これで記事一覧がlocalhost:3000/postsで見れるようになっているはずです。
記事詳細
続いて記事詳細も作っていきましょう。
こちらに関してもやることは一緒です。
以下のファイルを作成します。
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<svelte:head>
<title>{data.title}</title>
</svelte:head>
<article>
<h1>{data.title}</h1>
<time datetime={data.publishedAt}>{data.publishedAt}</time>
{@html data.content}
<p><a href="/posts" sveltekit:prefetch>Back To Posts</a></p>
</article>
記事一覧でdata.contents
に配列で入っていた内容をそのまま出しているだけですね。
import { error } from '@sveltejs/kit';
import { client } from '../../../libs/client';
import type { MicroCMSObjectContent } from 'microcms-js-sdk';
type Post = {
id: string;
title: string;
content: string;
};
type Props = {
params: {
slug: string;
};
};
/** @type {import('@sveltejs/kit').RequestHandler} */
export async function load({ params }: Props) {
const res = await client.get<MicroCMSObjectContent & Post>({
endpoint: 'bar',
contentId: params.slug
});
if (res) {
return { ...res };
}
throw error(404, 'Not found');
}
やっていることは同じですが、load関数の引数にparam
を渡しています。
今回ディレクトリ名に[slug]とつけたので、
localhost:3000/posts/helloとした場合helloがslugに入ってきます。
テンプレート側はこれでおしまいです。
個人的にはindex.json.jsなどを置いていた時のほうがわかりやすかった気がしますね。
Cloudflare Pages
とりあえず登録してください。
登録したら左サイドバーにPagesというのがあるのでクリックして、プロジェクトを作成します。
Gitに接続をクリックするとGitHubアカウントの選択と、リポジトリの選択項目が出てきます。
リポジトリが表示されていない場合は、
GitHub のCloudflare Pagesのアプリに対するリポジトリ アクセスを構成します。
の部分にリンクがあるので、デプロイしたいリポジトリを選択します。
選択するとリダイレクトがかかって元の画面に戻ってくるので、選択してセットアップの開始をクリックします。
遷移後のページでフレームワーク プリセットをSvelteKitに選択すると、勝手に項目を埋めてくれます。
楽ちんですね!
環境変数 (アドバンスド)という項目があるので、さっき.env
に設定したものをこちらにも登録しましょう。
注意
ここで、NODE_VERSIONに17以上(16.17.0以上)を指定する必要があります。
バージョンをこれで指定します。
「保存してデプロイする」をクリックするとデプロイされます。
お疲れさまでした。
躓いた点
- envの読込は VITE_付きでないとだめ
https://kit.svelte.dev/docs/modules#$env-dynamic-private
この辺を使うのかと思った。使ってみたがうまくいかなかった。 - env指定時、VITE_付きで指定するとビルド後のファイルに残ってしまうのでは?と思ったのだが、+page.server.tsで使用するとビルド時に取得後のデータをjsonで持つようになるので問題なかった。(デプロイされたサイトのソースを見てみてください)
- データ取得
+page.svelteでAPIをたたく関数を作って取得するとビルド後のファイルにenvで設定したキーがそのまま残ってしまう - ルーティングが変わっているのに気づいていなかった
https://kit.svelte.dev/docs/routing
解決できていない問題
svelte.config.jsでconfig.kit.trailingSlashを'always'にすると/posts/ページのみリロードで404になる
ローカルでのビルドでもpostsディレクトリにindex.htmlが生成されているので404にはならないと思うのですが、なぜか404になります。
他にも設定項目があるので何か設定漏れがあるのかもしれませんが、本記事では解決できていないのでtrailingSlashを設定していません。
感想
個人ブログもCloudflare Pagesにデプロイしているのですが、アナリティクスも設定できたりR2というストレージに画像もアップできそうなのでいいなと思っています。
Cloudflare Workersを使えばBASIC認証もかけられるので便利です。
使ってみてはどうでしょうか?