Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

スタッフディレクトリアプリケーションを構築する

最終更新 Markdown で表示Agent セットアップ

このチュートリアルでは、D1 を使ってスタッフディレクトリを構築します。このアプリケーションでは、組織の従業員情報を参照でき、管理者はアプリ内で新しい従業員を追加できます。 そのためには、まず D1 データベース を用意してデータを管理し、HonoX FrameworkCloudflare Pages でアプリケーションを開発・デプロイします。

前提条件

このチュートリアルを進める前に、次を用意してください。

  • Cloudflare アカウント。未登録の場合は、先に サインアップ してください。
  • 比較的新しいバージョンの npm をインストール済みであること。

今はセットアップを進めない場合は、GitHub で 完成済みのコード を参照できます。

1. HonoX をインストールする

このチュートリアルでは、フルスタックの Web サイトと Web API を作るメタフレームワーク HonoX を使います。プロジェクトで HonoX を使うには、hono-create コマンドを実行します。

次のコマンドで開始します。

npm create hono@latest

セットアップ中に、プロジェクトディレクトリの名前とテンプレートの選択を求められます。選択するときは x-basic テンプレートを選びます。

2. HonoX アプリケーションを初期化する

プロジェクトのセットアップが終わると、次のような生成ファイルの一覧を確認できます。これは HonoX アプリケーションの典型的な構成です。

.
├── app
│   ├── global.d.ts // global type definitions
│   ├── routes
│   │   ├── _404.tsx // not found page
│   │   ├── _error.tsx // error page
│   │   ├── _renderer.tsx // renderer definition
│   │   ├── about
│   │   │   └── [name].tsx // matches `/about/:name`
│   │   └── index.tsx // matches `/`
│   └── server.ts // server entry file
├── package.json
├── tsconfig.json
└── vite.config.ts

プロジェクトには、アプリ本体、ルート、サーバー設定のディレクトリに加え、パッケージ管理、TypeScript、Vite 用の設定ファイルがあります。

3. データベースを作成する

プロジェクト用のデータベースを作成するには、Cloudflare の CLI ツール Wrangler を使います。D1 の操作には wrangler d1 コマンドが使えます。次のコマンドで、staff-directory という名前のデータベースを作成します。

npx wrangler d1 create staff-directory

データベースを作成したら、アプリケーションとデータベースを連携するために、Wrangler 設定ファイルバインディング を設定します。

このバインディングにより、アプリケーションから D1 データベース、KV 名前空間、R2 バケットなどの Cloudflare リソースを操作できます。設定するには、プロジェクトのルートディレクトリに Wrangler ファイルを作成し、基本情報を入力します。

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "staff-directory",
	// Set this to today's date
	"compatibility_date": "2026-09-20"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "staff-directory"
# Set this to today's date
compatibility_date = "2026-09-20"

次に、データベースのバインディング詳細を Wrangler ファイルに追加します。アプリケーション内でデータベースを参照するバインディング名(ここでは DB)と、データベース作成時に表示された database_name および database_id を指定します。

{
	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "staff-directory",
			"database_id": "f495af5f-dd71-4554-9974-97bdda7137b3"
		}
	]
}
[[d1_databases]]
binding = "DB"
database_name = "staff-directory"
database_id = "f495af5f-dd71-4554-9974-97bdda7137b3"

これで、コマンドラインからもコード内からも、D1 データベースにアクセスして操作できるようになりました。

あわせて、vite.config.js の Vite 設定も調整します。ローカル環境で Cloudflare バインディングを正しく扱うために、次の設定を追加します。

import adapter from "@hono/vite-dev-server/cloudflare";

export default defineConfig(({ mode }) => {
	if (mode === "client") {
		return {
			plugins: [client()],
		};
	} else {
		return {
			plugins: [
				honox({
					devServer: {
						adapter,
					},
				}),
				pages(),
			],
		};
	}
});

4. D1 を操作する

D1 データベースを操作するには、wrangler d1 execute コマンドで SQL を直接実行できます。

wrangler d1 execute staff-directory --command "SELECT name FROM sqlite_schema WHERE type ='table'"

このコマンドで、コマンドラインから直接クエリや操作を実行できます。

初期データの投入やバッチ処理などでは、コマンドを書いた SQL ファイルを渡せます。そのためには、プロジェクトのルートディレクトリに schema.sql を作成し、SQL クエリを書き込みます。

CREATE TABLE locations (
    location_id INTEGER PRIMARY KEY AUTOINCREMENT,
    location_name VARCHAR(255) NOT NULL
);

CREATE TABLE departments (
    department_id INTEGER PRIMARY KEY AUTOINCREMENT,
    department_name VARCHAR(255) NOT NULL
);

CREATE TABLE employees (
    employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(255) NOT NULL,
    position VARCHAR(255) NOT NULL,
    image_url VARCHAR(255) NOT NULL,
    join_date DATE NOT NULL,
    location_id INTEGER REFERENCES locations(location_id),
    department_id INTEGER REFERENCES departments(department_id)
);

INSERT INTO locations (location_name) VALUES ('London, UK'), ('Paris, France'), ('Berlin, Germany'), ('Lagos, Nigeria'), ('Nairobi, Kenya'), ('Cairo, Egypt'), ('New York, NY'), ('San Francisco, CA'), ('Chicago, IL');

INSERT INTO departments (department_name) VALUES ('Software Engineering'), ('Product Management'), ('Information Technology (IT)'), ('Quality Assurance (QA)'), ('User Experience (UX)/User Interface (UI) Design'), ('Sales and Marketing'), ('Human Resources (HR)'), ('Customer Support'), ('Research and Development (R&D)'), ('Finance and Accounting');

上記のクエリで LocationsDepartmentsEmployees の 3 つのテーブルを作成します。初期データを入れるには INSERT INTO を使います。スキーマファイルの準備ができたら、D1 データベースに適用します。実行するスキーマファイルは --file フラグで指定します。

wrangler d1 execute staff-directory --file=./schema.sql

ローカルでスキーマを実行し、ローカルディレクトリにデータを投入するには、上記コマンドに --local フラグを付けます。

5. SQL ステートメントを作成する

これまでの手順で D1 データベースを用意し、Wrangler ファイルを設定すると、コードからは DB バインディング経由でデータベースにアクセスできます。SQL ステートメントを準備して実行し、データベースを直接操作できます。次のステップでは、このバインディングでデータの取得や新規レコードの挿入など、よく使う操作を行います。

データベースからデータを取得する

export const findAllEmployees = async (db: D1Database) => {
	const query = `
      SELECT employees.*, locations.location_name, departments.department_name
      FROM employees
      JOIN locations ON employees.location_id = locations.location_id
      JOIN departments ON employees.department_id = departments.department_id
      `;
	const { results } = await db.prepare(query).run();
	const employees = results;
	return employees;
};

データベースにデータを挿入する

export const createEmployee = async (db: D1Database, employee: Employee) => {
	const query = `
      INSERT INTO employees (name, position, join_date, image_url, department_id, location_id)
      VALUES (?, ?, ?, ?, ?, ?)`;

	const results = await db
		.prepare(query)
		.bind(
			employee.name,
			employee.position,
			employee.join_date,
			employee.image_url,
			employee.department_id,
			employee.location_id,
		)
		.run();
	const employees = results;
	return employees;
};

アプリケーションで使うクエリの一覧は、コードベースの db.ts を参照してください。

6. UI を開発する

このアプリケーションは描画に hono/jsx を使います。JSX レンダラーのミドルウェアを使い、app/routes/_renderer.tsx に Renderer を設定します。これがアプリケーションのエントリポイントになります。

import { jsxRenderer } from 'hono/jsx-renderer'
import { Script } from 'honox/server'

export default jsxRenderer(({ children, title }) => {
  return (
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>{title}</title>
        <Script src="/app/client.ts" async />
      </head>
      <body>{children}</body>
    </html>
  )
})

先に定義したバインディングを、TypeScript のグローバル型定義がある global.d.ts に追加し、アプリケーション全体で型の一貫性を保ちます。

declare module "hono" {
	interface Env {
		Variables: {};
		Bindings: {
			DB: D1Database;
		};
	}
}

このアプリケーションはスタイルに Tailwind CSS を使います。Tailwind CSS を使うには、TailwindCSS のドキュメント を参照するか、GitHub の手順 に従ってください。

従業員一覧を表示するには、db.tsfindAllEmployees 関数を呼び出し、routes/index.tsx から使います。ファイル内の createRoute() は、GETPOSTPUTDELETE など異なる HTTP メソッドを扱うルートを定義するためのヘルパーです。

import { css } from 'hono/css'
import { createRoute } from 'honox/factory'
import Counter from '../islands/counter'

const className = css`
  font-family: sans-serif;
`

export default createRoute((c) => {
  const name = c.req.query('name') ?? 'Hono'
  return c.render(
    <div class={className}>
      <h1>Hello, {name}!</h1>
      <Counter />
    </div>,
    { title: name }
  )
})

ファイル内の既存コードには、Counter コンポーネントを使ったプレースホルダーがあります。この部分を次のコードブロックに置き換えます。

import { createRoute } from 'honox/factory'
import type { FC } from 'hono/jsx'
import type { Employee } from '../db'
import { findAllEmployees, findAllDepartments, findAllLocations } from '../db'

const EmployeeCard: FC<{ employee: Employee }> = ({ employee }) => {
  const { employee_id, name, image_url, department_name, location_name } = employee;
  return (
    <div className="max-w-sm bg-white border border-gray-200 rounded-lg shadow-md">
      <a href={`/employee/${employee_id}`}>
        <img className="bg-indigo-600 p-4 rounded-t-lg" src={image_url} alt={name} />
        //...
      </a>
    </div>
  );
};

export const GET = createRoute(async (c) => {
  const employees = await findAllEmployees(c.env.DB)
  const locations = await findAllLocations(c.env.DB)
  const departments = await findAllDepartments(c.env.DB)
  return c.render(
    <section className="flex-grow">
      <h1 className="mb-4 text-3xl font-extrabold text-gray-900 dark:text-white md:text-5xl lg:text-6xl mt-12">
        <span className="text-transparent bg-clip-text bg-gradient-to-r to-blue-600 from-sky-400">{`Directory `}</span>
      </h1>
      //...
      </section>
      <section className="flex flex-wrap -mx-4">
        {employees.map((employee) => (
          <div className="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
            <EmployeeCard employee={employee} />
          </div>
        ))}
      </section>
    </section>
  )
})

このスニペットでは、db.ts から findAllEmployeesfindAllLocationsfindAllDepartments をインポートし、バインディング c.env.DB で呼び出しています。これで取得したデータをページに表示できます。

従業員を追加する

/admin ページから新しい従業員を作成するには、export POST ルートを使います。

import { createRoute } from "honox/factory";
import type { Employee } from "../../db";
import { getFormDataValue, getFormDataNumber } from "../../utils/formData";
import { createEmployee } from "../../db";

export const POST = createRoute(async (c) => {
	try {
		const formData = await c.req.formData();
		const imageFile = formData.get("image_file");
		let imageUrl = "";

		// TODO: process image url with R2

		const employeeData: Employee = {
			employee_id: getFormDataValue(formData, "employee_id"),
			name: getFormDataValue(formData, "name"),
			position: getFormDataValue(formData, "position"),
			image_url: imageUrl,
			join_date: getFormDataValue(formData, "join_date"),
			department_id: getFormDataNumber(formData, "department_id"),
			location_id: getFormDataNumber(formData, "location_id"),
			location_name: "",
			department_name: "",
		};

		await createEmployee(c.env.DB, employeeData);
		return c.redirect("/", 303);
	} catch (error) {
		return new Response("Error processing your request", { status: 500 });
	}
});

画像を R2 に保存する

新しい従業員を作成するとき、アップロードした画像はデータベースへ追加する前に R2 バケットへ保存できます。

画像を R2 バケットに保存する手順は次のとおりです。

  1. R2 バケットを作成します。
  2. 画像をこのバケットにアップロードします。
  3. バケットから画像の公開 URL を取得します。この URL をデータベースに保存し、R2 バケット内の画像へリンクします。

バケットの作成には wrangler r2 bucket create コマンドを使います。

wrangler r2 bucket create employee-avatars

バケットを作成したら、Wrangler ファイルに R2 バケットのバインディングを追加します。

{
	"r2_buckets": [
		{
			"binding": "MY_BUCKET",
			"bucket_name": "employee-avatars"
		}
	]
}
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "employee-avatars"

R2 のバインディングを global.d.ts に渡します。

declare module "hono" {
	interface Env {
		Variables: {};
		Bindings: {
			DB: D1Database;
			MY_BUCKET: R2Bucket;
		};
	}
}

アップロードした画像を R2 バケットに保存するには、R2 の put() メソッドを使えます。このメソッドで画像ファイルをバケットにアップロードします。

if (imageFile instanceof File) {
	const key = `${new Date().getTime()}-${imageFile.name}`;
	const fileBuffer = await imageFile.arrayBuffer();

	await c.env.MY_BUCKET.put(key, fileBuffer, {
		httpMetadata: {
			contentType: imageFile.type || "application/octet-stream",
		},
	});
	console.log(`File uploaded successfully: ${key}`);
	imageUrl = `https://pub-8d936184779047cc96686a631f318fce.r2.dev/${key}`;
}

コードベース全体は GitHub を参照 してください。

7. HonoX アプリケーションをデプロイする

デプロイの準備ができたら、Wrangler でプロジェクトをビルドし、Cloudflare ネットワークへデプロイできます。wrangler whoami を実行し、Cloudflare アカウントにログインしていることを確認します。未ログインの場合、Wrangler がログインを求め、コンピューターから自動で認証済みリクエストを送るための API キーを作成します。

ログイン後、Wrangler ファイルが次のコードブロックと同様に設定されていることを確認します。

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "staff-directory",
	// Set this to today's date
	"compatibility_date": "2026-09-20",
	"r2_buckets": [
		{
			"binding": "MY_BUCKET",
			"bucket_name": "employee-avatars"
		}
	],
	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "staff-directory",
			"database_id": "f495af5f-dd71-4554-9974-97bdda7137b3"
		}
	]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "staff-directory"
# Set this to today's date
compatibility_date = "2026-09-20"

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "employee-avatars"

[[d1_databases]]
binding = "DB"
database_name = "staff-directory"
database_id = "f495af5f-dd71-4554-9974-97bdda7137b3"

wrangler deploy を実行し、プロジェクトを Cloudflare にデプロイします。デプロイ後、表示された URL にアクセスして動作を確認できます。ブラウザーに、作成した基本的なフロントエンドが表示されます。データベースにデータがなければ、/admin ページで新しい従業員を追加します。ホームページに新しい従業員が表示されます。

まとめ

このチュートリアルでは、組織内の全従業員を閲覧できるスタッフディレクトリアプリケーションを構築しました。ソースコード全体は Staff directory リポジトリ を参照してください。

スタッフディレクトリのデモ

役に立ちましたか?