Nuxt Contentを用いたドキュメントベースのバイブコーディング①

に公開

こんにちは。
Nuxt Contentをご存知でしょうか。
Nuxt ContentとはNuxt アプリケーション向けのファイルベース CMS モジュールで、これはとてもバイブコーディングに適していると考えています。

まず、多くの人が感じているように、バイブコーディングで大切になるのはドキュメントの管理です。
AIにすべてを任せたら思わぬ方針で作業を開始してしまった…。という経験をした人は多いのではないでしょうか。現時点では、まずはドキュメントを整備し、その上でAIを走らせるのが結果として最短になると私は考えます。

この記事では、この管理するドキュメントを /root/docs に配置します。そしてそれをNuxt ContentでWeb公開までしてしまおう!といった記事になります。

なお、Webで公開するメリットとして開発を行わないマネジメント層などのメンバーも簡単に閲覧することができるようになります。
もちろんmdファイルなんだから、GitHub上でそのソースをそのまま見ればよいという意見もあるかもしれません。しかし、GitHubの読みやすさは GitHub UI に依存し、デザインやナビゲーションの柔軟性は乏しいです。そこで Nuxt Content を利用することで、チームメンバーが使いやすい独自のUIカスタマイズ、そしてより検索しやすい恩恵を得ることができます。

AIにも開発者にも読みやすいmdファイルを、開発に直接関わらない人にまで読みやすくし、それをプロジェクト全体で共有していくことをNuxt Contentで実現します。

※ アクセス制限などの話は今回は記載しない予定です。

root
├── docs // ★nuxt-contentによるドキュメント
└── src  // ★好きな言語・好きなフレームワークによるソース

環境

Ubuntu 24.04 (WSL2)
Nuxt 4.1.2
Node: v24.9.0
パッケージマネージャー: pnpm

インストール

では実際に導入していきましょう。

  1. プロジェクト名docsでcreate
bash
pnpm create nuxt docs
  1. create中に公式モジュールのインストールを聞かれるのでYesを選択

✔ Would you like to install any of the official modules?
Yes

そして @nuxt/content と他必要な公式モジュールにチェックをつけてEnter

  1. better-sqlite3をインストールするか質問されるためYesと回答

✔ Do you want to install better-sqlite3 package?
Yes

  1. 開発サーバーの起動
bash
cd docs
pnpm run dev

これでエラーが起きず立ち上がればセットアップ完了です。

My First Pageの作成

Nuxt Contentの公式ドキュメントに従いMy First Pageを作成します。

  1. docs配下にcontent.config.tsを作成
content.config.ts
import { defineContentConfig, defineCollection } from '@nuxt/content'

export default defineContentConfig({
  collections: {
    content: defineCollection({
      type: 'page',
      source: '**/*.md'
    })
  }
})
  1. content/index.md に初めてのmdファイルを作成
content/index.md
# My First Page

Here is some content.
  1. app/pages/index.vueを作成
    まずpages/index.vueをnuxiコマンドで作成(楽なのでおすすめ)
bash
npx nuxi add page index

内容を書き換え

app/pages/index.vue
<script setup lang="ts">
const { data: home } = await useAsyncData(() => queryCollection('content').path('/').first())

useSeoMeta({
  title: home.value?.title,
  description: home.value?.description
})
</script>

<template>
  <ContentRenderer v-if="home" :value="home" />
  <div v-else>Home not found</div>
</template>
  1. ルーティングするようにapp.vueを設定
app.vue
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
  1. 開発サーバーにアクセス

テーブル定義書の作成

では実際にAI駆動開発を模して、Codexにテーブル定義書を作成してもらいました。

  1. 001.table/001.users.md にユーザーテーブルを作成
001.table/001.users.md
# Users テーブル

主に Web アプリケーションのユーザー情報を保持するためのテーブル定義。username はログインや検索時に利用されるためユニーク制約を付与する。

## カラム定義

| カラム名      || NOT NULL | デフォルト        | 説明                                               |
| ------------- | ---------------- | -------- | ----------------- | -------------------------------------------------- |
| id            | BIGINT UNSIGNED  | Yes     | AUTO_INCREMENT    | ユーザーを一意に識別する主キー。                   |
| username      | VARCHAR(50)      | Yes     | なし              | ログインに利用するユーザー名。ユニーク制約を付与。 |
| email         | VARCHAR(255)     | Yes     | なし              | 連絡先メールアドレス。ユニーク制約を付与。         |
| display_name  | VARCHAR(100)     | Yes     | なし              | 画面表示用の名前。                                 |
| password_hash | VARCHAR(255)     | Yes     | なし              | ハッシュ済みパスワード。                           |
| status        | TINYINT UNSIGNED | Yes     | 1                 | アカウント状態。1: 有効、0: 無効 など。            |
| last_login_at | DATETIME         | No   | NULL              | 最終ログイン日時。                                 |
| created_at    | DATETIME         | Yes     | CURRENT_TIMESTAMP | レコード作成日時。                                 |
| updated_at    | DATETIME         | Yes     | CURRENT_TIMESTAMP | レコード更新日時。ON UPDATE CURRENT_TIMESTAMP。    |

## インデックス

- PRIMARY KEY (id)
- UNIQUE KEY uq_user_username (username)
- UNIQUE KEY uq_user_email (email)
- INDEX idx_user_status (status)

## 備考

- status はアカウント停止やメール未確認など複数の状態を表現できるよう ENUM ではなく数値で管理する。
- メールアドレス変更フローを確実にするため、メール確認済みかどうかを管理したい場合は email_verified_at DATETIME などのカラムを追加する。
- 監査ログが必要な場合は別テーブルで操作履歴を保持する。

  1. pageのvueファイルを作成
bash
npx nuxi add page table/[...slug].vue
[...slug].vue
<script lang="ts" setup>
const route = useRoute();
const { data: page } = await useAsyncData(route.path, () => {
  return queryCollection("content").path(route.path).first();
});
</script>

<template>
  <div>
    <ContentRenderer v-if="page" :value="page" />
    <div v-else>Page not found</div>
  </div>
</template>

  1. localhost:3000/table/users にアクセス
bash
pnpm run dev

開発サーバー上でテーブル定義書をみることができました。
次はレイアウトの修正・記事の検索について記載していきます。

Discussion