Cloudflare Workers / D1 / Pages / R2 だけで作るブログサービスの構築方法

近年、サーバーレスアーキテクチャの普及に伴い、外部の大規模なバックエンドに依存せず、Cloudflare のエコシステム内でブログサービスを完結させる取り組みが注目されています。本記事では、**Cloudflare Workers**、**Cloudflare D1**、**Cloudflare Pages**、**Cloudflare R2** を組み合わせたブログサービスの構築方法について、サンプルコードとともに解説いたします。また、各サービスの無料枠や想定されるコストについてもご説明いたします。

---

## 1. 各サービスの役割と特徴

### 1.1 Cloudflare Workers  
- **概要**：  
  グローバルなエッジネットワーク上で動作するサーバーレス実行環境です。  
- **役割**：  
  ブログのバックエンド API（記事の作成・取得・更新・削除など）を実装します。  
- **特徴**：  
  高速なレスポンスとスケーラビリティを実現でき、エッジでの処理が可能です。

### 1.2 Cloudflare D1  
- **概要**：  
  SQLite をベースとしたリレーショナルデータベースサービスです。  
- **役割**：  
  ブログ記事、ユーザー情報、コメントなどのデータを永続化するために利用します。  
- **特徴**：  
  Workers との連携が容易ですが、複雑なトランザクション管理や大規模なデータ運用が必要な場合は、外部サービスとの連携も検討します。

### 1.3 Cloudflare Pages  
- **概要**：  
  Next.js などで構築したフロントエンド（静的サイトまたはハイブリッドサイト）のホスティングサービスです。  
- **役割**：  
  API から取得したデータをもとに、ブログのトップページや記事一覧、詳細ページを表示します。  
- **特徴**：  
  CDN を利用したグローバル配信と簡易なビルド・デプロイフローが魅力です。

### 1.4 Cloudflare R2  
- **概要**：  
  画像や動画などのメディアコンテンツのオブジェクトストレージサービスです。  
- **役割**：  
  ブログ記事に添付する画像などのメディアを保存・配信します。  
- **特徴**：  
  約10GBの無料枠が提供され、超過時も低コスト（例：\$0.015/GB）で利用可能です。

---

## 2. サンプルコードで学ぶブログサービスの実装

ここでは、Cloudflare Workers と D1 を用いたバックエンド API と、Next.js を利用したフロントエンドのサンプルコードを紹介いたします。

### 2.1 Cloudflare Workers と D1 を使った API 実装

#### 2.1.1 Wrangler 設定ファイル（wrangler.toml）

```toml
name = "my-blog-worker"
type = "javascript"

[vars]
# 必要に応じて環境変数を定義

[build]
command = "npm run build"

[[d1_databases]]
binding = "DB"         # Workers 内で参照する際の名称
database_name = "my_blog_database"
```

#### 2.1.2 API 実装例（index.ts）

以下は、Hono フレームワークを利用したシンプルな API のサンプルです。  
この例では、記事一覧の取得、単一記事の取得、記事の投稿を実装しています。

```typescript
// index.ts
import { Hono } from 'hono';

interface Env {
  DB: D1Database; // Cloudflare D1 のバインディング
}

const app = new Hono<{ Bindings: Env }>();

// 記事一覧を取得するエンドポイント（GET /posts）
app.get('/posts', async (c) => {
  const { DB } = c.env;
  const result = await DB.prepare('SELECT * FROM posts ORDER BY created_at DESC').all();
  return c.json(result.results);
});

// 単一記事の取得（GET /posts/:id）
app.get('/posts/:id', async (c) => {
  const { DB } = c.env;
  const id = c.req.param('id');
  const result = await DB.prepare('SELECT * FROM posts WHERE id = ?').bind(id).first();
  if (!result) {
    return c.json({ error: '記事が見つかりません' }, 404);
  }
  return c.json(result);
});

// 記事の投稿（POST /posts）
// ※本番環境では認証や入力検証を実装してください
app.post('/posts', async (c) => {
  const { DB } = c.env;
  const data = await c.req.json();
  const stmt = DB.prepare('INSERT INTO posts (title, content, created_at) VALUES (?, ?, ?)');
  const result = await stmt.bind(data.title, data.content, new Date().toISOString()).run();
  return c.json({ id: result.lastRowId });
});

export default app;
```

上記のコードを Workers にデプロイすることで、エッジ上で動作する API サーバーが実現できます。  
なお、画像データの管理やアップロード処理については、Cloudflare R2 を利用して別途実装することも可能です。

### 2.2 Next.js を使ったフロントエンド実装（Cloudflare Pages）

Cloudflare Pages にデプロイするフロントエンドでは、Workers 上の API からデータを取得し、ブログ記事一覧や詳細ページを表示します。  
以下は、記事一覧ページのサンプルコードです。

```javascript
// pages/index.js
import React from 'react';

export async function getStaticProps() {
  // Workers にデプロイした API エンドポイントの URL を指定します
  const res = await fetch('https://<your-workers-subdomain>.workers.dev/posts');
  const posts = await res.json();
  return {
    props: { posts },
    revalidate: 60, // Incremental Static Regeneration（60秒ごとに再生成）
  };
}

const HomePage = ({ posts }) => (
  <div>
    <h1>ブログ記事一覧</h1>
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <a href={`/posts/${post.id}`}>{post.title}</a>
        </li>
      ))}
    </ul>
  </div>
);

export default HomePage;
```

このように、Cloudflare Pages でホスティングする Next.js アプリケーションは、API 経由で最新記事データを取得し、グローバルな CDN による高速配信を実現します。

---

## 3. コストと無料枠について

Cloudflare の各サービスは、比較的充実した無料枠が提供されているため、中小規模のブログ運用であればほぼ無料、または非常に低コストで実現できます。

### 3.1 Cloudflare Workers  
- **無料枠**：  
  毎月数百万リクエストまで無料で利用可能です。  
- **超過時の料金**：  
  従量課金制となるため、アクセスが非常に多い場合のみ料金が発生します。

### 3.2 Cloudflare D1  
- **無料枠**：  
  現在はプレビュー（ベータ）段階で無料利用が可能です。  
- **注意点**：  
  高度なデータベース機能が必要な場合は、外部サービスとの連携も検討してください。

### 3.3 Cloudflare Pages  
- **無料枠**：  
  静的サイトのホスティングに関しては、非常に寛大な無料枠が提供されています。  
- **超過時の料金**：  
  通常のブログ運営では追加料金が発生するケースは少ないです。

### 3.4 Cloudflare R2  
- **無料枠**：  
  約10GBのストレージと一定量のデータ転送が無料で利用可能です。  
- **超過時の料金**：  
  超過分は月額約 \$0.015/GB 程度の料金が発生します。

---

## 4. まとめ

本記事では、**Cloudflare Workers**、**Cloudflare D1**、**Cloudflare Pages**、**Cloudflare R2** を活用して、外部サービスに依存せずにブログサービスを構築する方法をサンプルコードと共に解説いたしました。

- **Workers**  
  エッジネットワーク上で API エンドポイントを実装し、迅速なレスポンスを実現。

- **D1**  
  SQLite ベースのシンプルなリレーショナルデータベースで、記事データなどを永続化。

- **Pages**  
  Next.js を用いたフロントエンドをグローバルに高速配信。

- **R2**  
  画像などのメディアコンテンツを効率的に管理・配信。

各サービスともに充実した無料枠があり、中小規模のブログ運用であれば、ほぼ無料で実現可能です。アクセス数やストレージ利用量が増加した場合は従量課金が適用されますが、現実的な規模であれば非常に低コストで運用できます。