2025-06-05T00:00:00.000Z
CloudFlare Workers + Nuxt Content
ブログを作成するにあたり、CloudFlare WorkersとNuxt Contentの連携に苦しめられたのでここに書き残しておきます。
テスト環境
- Windows 11 Pro
- Nuxt v3.17
- Nuxt Content v3.5.1
- wrangler v4.18.0
現象
wrangler dev, CloudFlare Workers上でコンテンツを取得しようとすると直打ちや再読み込みをするとエラーが起きます。例えば次のようなコード
const { data: article } = await useAsyncData('docs-${route.path}', () => {
return queryCollection('docs').path(route.path).first()
})
簡単な解決策
解決策の概要だけ知りたいよという方
CloudFlare D1を用いる方法
私はこの方法を採用しました。 詳細はNuxt Content公式リファレンス
https://content.nuxt.com/docs/getting-started/configuration
を見てください。
export default defineNuxtConfig({
content: {
database: {
type: 'd1',
bindingName: 'CF_BINDING_NAME'
}
}
})
Prerendererを用いる方法
Server Side Rendering(SSR)で失敗するなら事前に生成しておけば良いではないか作戦第一弾 詳しくは
https://nuxt.com/docs/getting-started/prerendering
// /article/[id] 形式の場合
export default defineNuxtConfig({
nitro: {
prerender: {
routes: [
"/article/1",
"/article/2",
"/article/3"
],
},
},
});
Server Side Generation(SSG)を用いるやり方
これでもいけるはず。やってないのでわからないよ
検証
正直記事としては上だけで間に合っている気はしますが、どのようなエラーになるのかを書いておきます。
準備
詳細はCloudFlare Docsから https://developers.cloudflare.com/workers/frameworks/framework-guides/nuxt/
pnpm create cloudflare@latest nuxtcontent-test --framework=nuxt
このとき
undefined + @nuxt/content – The file-based CMS with support for Markdown, YAML, JSON
にチェックを入れておきましょう。―と言いたいのですがチェックをいれると何故かエラーが発生するので何も選択しなくていいです。git, deployはどちらでも。
Nuxt Contentをインストールしましょう
pnpm add @nuxt/content
また nuxt.config.tsのモジュールにNuxt Contentを追加します。
export default defineNuxtConfig({
modules: [
'@nuxt/content',
]
})
content.config.tsを作成、編集します
import { defineContentConfig, defineCollection } from '@nuxt/content'
export default defineContentConfig({
collections: {
content: defineCollection({
type: 'page',
source: '**/*.md'
})
}
})
pages/index.vueファイルを作成します。基本的にファイル名がURLに対応しますがindex.vueは例外的に/として扱われます。pages/hoge.vueならば/hogeです。次のように記述しておいてください。
<template>
<h1>links</h1>
<NuxtLink to="/md1">md1</NuxtLink>
<br>
<NuxtLink to="/md2">md2</NuxtLink>
</template>
pages/id.vueファイルを作成し、次のように記述しておきます。
<template>
<div v-if="article">
<ContentRenderer :value="article"/>
</div>
<div v-else>
<p>記事が取得できません</p>
</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const { data: article } = await useAsyncData('docs-${route.path}', () => {
return queryCollection('content').path(route.path).first()
})
</script>
またcontent/md1.md, content/md2.mdファイルを作成します。NuxtContentにて扱うデータ(.md, .yaml etc)はこのフォルダ内に収めることになります。の中の文字列は自由です。次のように記述しておいてください。
# Hello Markdown 1
app.vueを編集します。次のように書き換えてください。
<template>
<div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
この時点でフォルダ構成は
- nuxtcontent-test
- .nuxt
- content
- md1.md
- md2.md
- node_modules
- pages
- index.vue
- id.vue
- public
- server
- app.vue
- nuxt.config.ts
- content.config.ts
- tsconfig.json
- wrangler.jsonc
みたいな感じになっていると思います。
ここでビルドしたいところなのですが、そのままではbetter_sqlite3がなんたらと怒られ失敗するので
pnpm approve-builds
を実行しておきます。そして実行
pnpm run build
pnpm wrangler dev
実行するとmd1,md2へのリンクがみれると思います。クリックして飛んでみましょう。Markdownがしっかり描画されていると思います。
エラーの再現
ここからが本題です。URL直打ちしてみてください。(http://127.0.0.1:8787/md1 のように)、MarkDownが取得できていませんよね。useAsyncDataでerrorを受け取ってみます。
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const { data: article, error} = await useAsyncData('docs-${route.path}', () => {
return queryCollection('content').path(route.path).first()
})
if (error.value) {
console.error('Error fetching article:', error.value)
}
</script>
次のようなエラーが発生していると思います。
Error fetching article: ks: [POST] "/__nuxt_content/content/query?v=v3.5.0--VGqzQN-yc2GMP6_CYEq8MhIiMcSaqFsLtkMBN00JDjE": 500 Server Error
このようなエラーが発生している原因はWorkers環境でファイルシステムのアクセスが制限され、SQLiteが実行できないからぽいです。
一応WorkersにはNode.js互換レイヤーnodejs_compatがありますが十分でないようです。
つまり解決にはファイルシステムに依存しないデータベースを用いればよいわけです。D1とかね。
またもう一つのアプローチとして事前にレンダリングすることも有用です。実はこのコードpnpm run devとかなら問題なく動作します。queryCollectionが動く環境下で事前に生成すればよいのです。先に上げたプリレンダリングとかSSGとか。
おわりに
この問題、公式リファレンス-Configurationにある
By default Nuxt Content uses a local SQLite database to store and query content. If you like to use another database or you plan to deploy on Cloudflare Workers, you can modify this option.
の文言に気づけれるかが全てだったように思います。私は数時間を無駄にしました。
それではよきNuxtライフを
以上