swallowkit 0.1.0-alpha.1 → 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +529 -402
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +127 -143
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/deploy.d.ts +3 -0
  6. package/dist/cli/commands/deploy.d.ts.map +1 -0
  7. package/dist/cli/commands/deploy.js +147 -0
  8. package/dist/cli/commands/deploy.js.map +1 -0
  9. package/dist/cli/commands/dev.d.ts.map +1 -1
  10. package/dist/cli/commands/dev.js +114 -198
  11. package/dist/cli/commands/dev.js.map +1 -1
  12. package/dist/cli/commands/index.d.ts +1 -1
  13. package/dist/cli/commands/index.d.ts.map +1 -1
  14. package/dist/cli/commands/index.js +3 -4
  15. package/dist/cli/commands/index.js.map +1 -1
  16. package/dist/cli/commands/init.d.ts.map +1 -1
  17. package/dist/cli/commands/init.js +517 -291
  18. package/dist/cli/commands/init.js.map +1 -1
  19. package/dist/cli/index.js +11 -8
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/core/config.d.ts.map +1 -1
  22. package/dist/core/config.js +3 -24
  23. package/dist/core/config.js.map +1 -1
  24. package/dist/database/base-model.d.ts +81 -0
  25. package/dist/database/base-model.d.ts.map +1 -0
  26. package/dist/database/base-model.js +120 -0
  27. package/dist/database/base-model.js.map +1 -0
  28. package/dist/database/client.d.ts +0 -4
  29. package/dist/database/client.d.ts.map +1 -1
  30. package/dist/database/client.js +10 -45
  31. package/dist/database/client.js.map +1 -1
  32. package/dist/database/runtime-check.d.ts +14 -0
  33. package/dist/database/runtime-check.d.ts.map +1 -0
  34. package/dist/database/runtime-check.js +26 -0
  35. package/dist/database/runtime-check.js.map +1 -0
  36. package/dist/index.d.ts +5 -11
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +12 -60
  39. package/dist/index.js.map +1 -1
  40. package/dist/types/index.d.ts +3 -24
  41. package/dist/types/index.d.ts.map +1 -1
  42. package/package.json +5 -4
  43. package/dist/api/rpc-handler.d.ts +0 -71
  44. package/dist/api/rpc-handler.d.ts.map +0 -1
  45. package/dist/api/rpc-handler.js +0 -205
  46. package/dist/api/rpc-handler.js.map +0 -1
  47. package/dist/cli/commands/generate.d.ts +0 -4
  48. package/dist/cli/commands/generate.d.ts.map +0 -1
  49. package/dist/cli/commands/generate.js +0 -238
  50. package/dist/cli/commands/generate.js.map +0 -1
  51. package/dist/generator/api-generator.d.ts +0 -53
  52. package/dist/generator/api-generator.d.ts.map +0 -1
  53. package/dist/generator/api-generator.js +0 -284
  54. package/dist/generator/api-generator.js.map +0 -1
  55. package/dist/generator/schema-parser.d.ts +0 -45
  56. package/dist/generator/schema-parser.d.ts.map +0 -1
  57. package/dist/generator/schema-parser.js +0 -198
  58. package/dist/generator/schema-parser.js.map +0 -1
  59. package/dist/generator/templates/azure-functions.d.ts +0 -15
  60. package/dist/generator/templates/azure-functions.d.ts.map +0 -1
  61. package/dist/generator/templates/azure-functions.js +0 -274
  62. package/dist/generator/templates/azure-functions.js.map +0 -1
  63. package/dist/generator/templates/default-server-functions.d.ts +0 -2
  64. package/dist/generator/templates/default-server-functions.d.ts.map +0 -1
  65. package/dist/generator/templates/default-server-functions.js +0 -67
  66. package/dist/generator/templates/default-server-functions.js.map +0 -1
  67. package/dist/hooks/server-function-registry.d.ts +0 -67
  68. package/dist/hooks/server-function-registry.d.ts.map +0 -1
  69. package/dist/hooks/server-function-registry.js +0 -153
  70. package/dist/hooks/server-function-registry.js.map +0 -1
  71. package/dist/hooks/useQuery.d.ts +0 -61
  72. package/dist/hooks/useQuery.d.ts.map +0 -1
  73. package/dist/hooks/useQuery.js +0 -147
  74. package/dist/hooks/useQuery.js.map +0 -1
  75. package/dist/hooks/useServerFn.d.ts +0 -27
  76. package/dist/hooks/useServerFn.d.ts.map +0 -1
  77. package/dist/hooks/useServerFn.js +0 -119
  78. package/dist/hooks/useServerFn.js.map +0 -1
  79. package/dist/schemas/example.d.ts +0 -657
  80. package/dist/schemas/example.d.ts.map +0 -1
  81. package/dist/schemas/example.js +0 -133
  82. package/dist/schemas/example.js.map +0 -1
  83. package/dist/server/todo-functions.d.ts +0 -21
  84. package/dist/server/todo-functions.d.ts.map +0 -1
  85. package/dist/server/todo-functions.js +0 -121
  86. package/dist/server/todo-functions.js.map +0 -1
package/README.md CHANGED
@@ -1,563 +1,690 @@
1
- # SwallowKit (暫定版)
1
+ # SwallowKit
2
2
 
3
- Azure Static Web Apps + Cosmos DB 専用フレームワーク
3
+ A type-safe, schema-driven development toolkit for Next.js applications on Azure, featuring seamless Zod schema sharing between frontend, backend, and Cosmos DB.
4
4
 
5
- > **注意**: これは暫定版のドキュメントです。API や機能は今後変更される可能性があります。
5
+ SwallowKit enables developers to build full-stack Next.js applications with external Azure Functions backends while maintaining end-to-end type safety through shared Zod schemas. Deploy your Next.js app to Azure Static Web Apps using standalone mode, and connect to independent Azure Functions for backend operations—all with consistent type definitions and validation.
6
6
 
7
- ## 🚀 特徴
7
+ > **Note**: This project is in active development. APIs may change in future versions.
8
8
 
9
- - **Cosmos DB 標準搭載**: Cosmos DB をデフォルトデータベースとして採用
10
- - **React Hooks ベース**: `useServerFn` / `callServerFn` でサーバー関数を簡単に呼び出し
11
- - **型安全**: TypeScript による完全な型安全性
12
- - **自動セットアップ**: 開発環境起動時に Cosmos DB を自動セットアップ
13
- - **Azure 最適化**: Azure Static Web Apps + Azure Functions v4 に最適化
14
- - **開発者体験**: シンプルなコマンドで開発開始
9
+ ## 🚀 Features
15
10
 
16
- ## 📋 前提条件
11
+ - **Zod Schema Sharing**: Share type-safe schemas across frontend, backend, and database layers
12
+ - **Type-Safe Cosmos DB**: Built-in Cosmos DB integration with automatic validation
13
+ - **Next.js Standalone Deployment**: Deploy to Azure Static Web Apps with standalone mode
14
+ - **External Backend Support**: Connect to independent Azure Functions backends
15
+ - **Repository Pattern**: Simple, type-safe data access with BaseModel and SchemaRepository
16
+ - **Full TypeScript**: End-to-end type safety from client to database
17
+ - **Azure Optimized**: Designed specifically for Azure Static Web Apps + Azure Functions + Cosmos DB
18
+ - **Developer Experience**: Simple CLI commands for development and deployment
19
+
20
+ ## 📋 Prerequisites
17
21
 
18
22
  - Node.js 22.x
19
- - Azure Cosmos DB Emulator (ローカル開発用)
20
- - Windows: [公式サイト](https://aka.ms/cosmosdb-emulator)からインストール
23
+ - Azure Cosmos DB Emulator (for local development)
24
+ - Windows: Install from [official site](https://aka.ms/cosmosdb-emulator)
21
25
  - Docker: `docker run -p 8081:8081 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator`
22
26
 
23
- ## 📦 インストール
27
+ ## 📦 Installation
24
28
 
25
29
  ```bash
26
- npm install swallowkit
30
+ npm install swallowkit next react react-dom zod
27
31
  ```
28
32
 
29
- ## 🛠️ クイックスタート
33
+ SwallowKit requires Next.js 14+ and Zod as peer dependencies.
34
+
35
+ ## 🛠️ Quick Start
30
36
 
31
- ### 1. プロジェクトの初期化
37
+ ### 1. Initialize SwallowKit Project
32
38
 
33
39
  ```bash
34
- npx swallowkit init my-todo-app
35
- cd my-todo-app
40
+ npx swallowkit init my-app
41
+ cd my-app
36
42
  npm install
37
43
  ```
38
44
 
39
- これにより以下が生成されます:
40
- - `src/` - React アプリケーション (Vite + React + TypeScript)
41
- - `src/serverFns.ts` - サーバー関数の型定義 (クライアント側スタブ)
42
- - `swallowkit.config.json` - SwallowKit 設定ファイル
45
+ This creates a complete full-stack project with:
46
+ - Next.js app with App Router, TypeScript, and Tailwind CSS
47
+ - Azure Functions project with HTTP triggers
48
+ - BFF (Backend For Frontend) API routes
49
+ - Example Todo app with Cosmos DB integration
50
+ - Greet function demonstrating BFF → Azure Functions pattern
43
51
 
44
- ### 2. Cosmos DB Emulator の起動
52
+ ### 2. Install Azure Functions Core Tools
53
+
54
+ To run Azure Functions locally, install the Azure Functions Core Tools:
45
55
 
46
56
  ```bash
47
- # Windowsの場合: スタートメニューから起動
48
- # Dockerの場合:
49
- docker run -p 8081:8081 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
57
+ npm install -g azure-functions-core-tools@4
50
58
  ```
51
59
 
52
- ### 3. 開発環境の起動
53
-
60
+ Or via chocolatey (Windows):
54
61
  ```bash
55
- npx swallowkit dev
62
+ choco install azure-functions-core-tools
56
63
  ```
57
64
 
58
- このコマンドは:
59
- 1. Cosmos DB Emulator の起動確認
60
- 2. Cosmos DB のデータベース・コンテナの自動作成 (冪等性あり)
61
- 3. Azure Functions API の自動ビルド
62
- 4. Vite 開発サーバーの起動
63
- 5. SWA CLI による統合環境の起動
65
+ ### 3. Start Development Environment
64
66
 
65
- 開発サーバーが起動したら、`http://localhost:4280` でアプリにアクセスできます。
67
+ The init command creates two projects:
68
+ - **Next.js app** (frontend + BFF API routes)
69
+ - **Azure Functions** (backend services in `functions/` directory)
66
70
 
67
- ## 📝 サーバー関数の実装
71
+ Start both servers with a single command:
68
72
 
69
- ### クライアント側の型定義 (`src/serverFns.ts`)
73
+ ```bash
74
+ npx swallowkit dev
75
+ ```
70
76
 
71
- ```typescript
72
- // クライアント側のスタブ - ブラウザでは実行されない
73
- interface Todo {
74
- id: string;
75
- text: string;
76
- completed: boolean;
77
- }
77
+ This will:
78
+ - Check and setup Cosmos DB Emulator
79
+ - Start Azure Functions (automatically installs dependencies if needed)
80
+ - ✅ Start Next.js development server
78
81
 
79
- export async function getTodos(): Promise<Todo[]> {
80
- throw new Error("This is a server function and should be called via useServerFn");
81
- }
82
+ The demo app will be available at:
83
+ - Next.js: http://localhost:3000
84
+ - Azure Functions: http://localhost:7071
82
85
 
83
- export async function addTodo({ text }: { text: string }): Promise<Todo> {
84
- throw new Error("This is a server function and should be called via useServerFn");
85
- }
86
+ **Options:**
87
+ ```bash
88
+ # Customize ports
89
+ npx swallowkit dev --port 3001 --functions-port 7072
86
90
 
87
- export async function deleteTodo({ id }: { id: string }): Promise<{ success: boolean }> {
88
- throw new Error("This is a server function and should be called via useServerFn");
89
- }
91
+ # Skip Azure Functions (Next.js only)
92
+ npx swallowkit dev --no-functions
90
93
 
91
- export async function toggleTodo({ id }: { id: string }): Promise<Todo | null> {
92
- throw new Error("This is a server function and should be called via useServerFn");
93
- }
94
+ # Open browser automatically
95
+ npx swallowkit dev --open
96
+
97
+ # Verbose output
98
+ npx swallowkit dev --verbose
94
99
  ```
95
100
 
96
- ### API の生成
101
+ ### 4. Project Structure
97
102
 
98
- ```bash
99
- npx swallowkit generate
103
+ ```
104
+ my-app/
105
+ ├── app/ # Next.js App Router
106
+ │ ├── api/ # BFF API Routes
107
+ │ │ └── greet/ # Example: Calls Azure Functions
108
+ │ │ └── route.ts
109
+ │ └── page.tsx # Homepage with demos
110
+ ├── components/ # React Components
111
+ │ ├── AddTodoForm.tsx # Todo form with Zod validation
112
+ │ ├── TodoItem.tsx # Todo item component
113
+ │ └── GreetingDemo.tsx # BFF → Functions demo
114
+ ├── lib/
115
+ │ ├── database/ # Cosmos DB client & repository
116
+ │ │ ├── base-model.ts # BaseModel class
117
+ │ │ ├── client.ts # DatabaseClient
118
+ │ │ └── repository.ts # SchemaRepository
119
+ │ ├── models/ # Zod schemas + models
120
+ │ │ └── todo.ts # Todo schema & model
121
+ │ └── server/ # Server actions
122
+ │ └── todos.ts # Todo CRUD operations
123
+ ├── functions/ # Azure Functions Project
124
+ │ ├── src/
125
+ │ │ └── functions/
126
+ │ │ └── greet.ts # Example HTTP trigger
127
+ │ ├── host.json
128
+ │ ├── local.settings.json
129
+ │ └── package.json
130
+ ├── .env.local # Environment variables
131
+ ├── swallowkit.config.js # SwallowKit configuration
132
+ └── staticwebapp.config.json # Azure SWA config
100
133
  ```
101
134
 
102
- これにより `api/` ディレクトリに以下が生成されます:
103
- - `api/src/shared/server-functions.ts` - Cosmos DB を使った実装
104
- - `api/src/functions/rpc.ts` - RPC エンドポイント (`/api/_swallowkit`)
105
- - Azure Functions v4 の設定ファイル
106
-
107
- **重要**: `server-functions.ts` は自動生成されますが、**ビジネスロジックを実装するファイル**です。
108
- 初回生成後はテンプレートをカスタマイズして使用してください。
135
+ ### 5. Define Shared Zod Schemas
109
136
 
110
- ### サーバー側の実装例 (`api/src/shared/server-functions.ts`)
137
+ Create type-safe schemas that will be used across your entire stack:
111
138
 
112
139
  ```typescript
113
- import { CosmosClient } from '@azure/cosmos';
114
-
115
- // Cosmos DB クライアントの初期化
116
- const endpoint = process.env.COSMOS_ENDPOINT || 'http://localhost:8081';
117
- const key = process.env.COSMOS_KEY || 'C2y6yDjf5/R+...'; // Emulator key
118
- const client = new CosmosClient({ endpoint, key });
140
+ // lib/schemas/todo.ts
141
+ import { z } from 'zod';
142
+
143
+ export const TodoSchema = z.object({
144
+ id: z.string(),
145
+ text: z.string().min(1, 'Todo text is required'),
146
+ completed: z.boolean().default(false),
147
+ createdAt: z.string().default(() => new Date().toISOString()),
148
+ });
119
149
 
120
- const database = client.database('swallowkit-db');
121
- const container = database.container('todos');
150
+ export type Todo = z.infer<typeof TodoSchema>;
151
+ ```
122
152
 
123
- interface Todo {
124
- id: string;
125
- text: string;
126
- completed: boolean;
127
- }
153
+ ### 5. Define Shared Zod Schemas
128
154
 
129
- export async function getTodos(): Promise<Todo[]> {
130
- const { resources } = await container.items.query('SELECT * FROM c').fetchAll();
131
- return resources as Todo[];
132
- }
155
+ Create type-safe schemas that will be used across your entire stack:
133
156
 
134
- export async function addTodo({ text }: { text: string }): Promise<Todo> {
135
- const newTodo: Todo = {
136
- id: Date.now().toString(),
137
- text,
138
- completed: false,
139
- };
140
- const { resource } = await container.items.create(newTodo);
141
- return resource as Todo;
142
- }
157
+ ```typescript
158
+ // lib/models/todo.ts
159
+ import { z } from 'zod';
160
+ import { BaseModel } from '@/lib/database/base-model';
161
+
162
+ export const todoSchema = z.object({
163
+ id: z.string(),
164
+ text: z.string().min(1, 'Todo text is required').max(200),
165
+ completed: z.boolean().default(false),
166
+ createdAt: z.string().default(() => new Date().toISOString()),
167
+ });
143
168
 
144
- export async function deleteTodo({ id }: { id: string }): Promise<{ success: boolean }> {
145
- await container.item(id, id).delete();
146
- return { success: true };
147
- }
169
+ export type TodoType = z.infer<typeof todoSchema>;
148
170
 
149
- export async function toggleTodo({ id }: { id: string }): Promise<Todo | null> {
150
- const { resource: todo } = await container.item(id, id).read<Todo>();
151
- if (todo) {
152
- todo.completed = !todo.completed;
153
- const { resource } = await container.item(id, id).replace(todo);
154
- return resource as Todo;
171
+ export class Todo extends BaseModel<TodoType> {
172
+ constructor() {
173
+ super('AppDatabase', 'Todos', todoSchema);
155
174
  }
156
- return null;
157
175
  }
158
176
  ```
159
177
 
160
- ## 🎯 React での使用
178
+ ### 6. Use BFF Pattern for Azure Functions
161
179
 
162
- ### クエリ用: `useServerFn`
180
+ Next.js API routes act as a BFF (Backend For Frontend) layer that calls Azure Functions:
163
181
 
164
- データ取得など、状態管理が必要な操作に使用:
165
-
166
- ```tsx
167
- import { useServerFn } from "swallowkit";
168
- import { getTodos } from "./serverFns";
182
+ ```typescript
183
+ // app/api/greet/route.ts
184
+ import { NextRequest, NextResponse } from 'next/server';
169
185
 
170
- function TodoList() {
171
- const { data: todos, loading, error, refetch } = useServerFn(getTodos, []);
186
+ const FUNCTIONS_BASE_URL = process.env.FUNCTIONS_BASE_URL || 'http://localhost:7071';
172
187
 
173
- if (loading) return <div>読み込み中...</div>;
174
- if (error) return <div>エラー: {error.message}</div>;
188
+ export async function GET(request: NextRequest) {
189
+ const { searchParams } = new URL(request.url);
190
+ const name = searchParams.get('name') || 'World';
175
191
 
176
- return (
177
- <div>
178
- <ul>
179
- {todos?.map((todo) => (
180
- <li key={todo.id}>{todo.text}</li>
181
- ))}
182
- </ul>
183
- <button onClick={refetch}>再読み込み</button>
184
- </div>
185
- );
192
+ // Call Azure Functions backend
193
+ const response = await fetch(`${FUNCTIONS_BASE_URL}/api/greet?name=${encodeURIComponent(name)}`);
194
+ const data = await response.json();
195
+
196
+ return NextResponse.json(data);
186
197
  }
187
198
  ```
188
199
 
189
- ### ミューテーション用: `callServerFn`
190
-
191
- 追加・更新・削除など、状態管理が不要な操作に使用:
200
+ ```typescript
201
+ // functions/src/functions/greet.ts
202
+ import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
203
+ import { z } from 'zod';
192
204
 
193
- ```tsx
194
- import { useServerFn, callServerFn } from "swallowkit";
195
- import { getTodos, addTodo, deleteTodo, toggleTodo } from "./serverFns";
205
+ const greetRequestSchema = z.object({
206
+ name: z.string().min(1).max(50),
207
+ });
196
208
 
197
- function TodoApp() {
198
- const [newTodoText, setNewTodoText] = useState("");
199
- const { data: todos, loading, error, refetch } = useServerFn(getTodos, []);
209
+ export async function greet(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
210
+ const name = request.query.get('name') || 'World';
211
+ const result = greetRequestSchema.safeParse({ name });
212
+
213
+ if (!result.success) {
214
+ return { status: 400, jsonBody: { error: result.error.errors[0].message } };
215
+ }
200
216
 
201
- const handleAddTodo = async () => {
202
- if (!newTodoText.trim()) return;
203
-
204
- await callServerFn(addTodo, { text: newTodoText });
205
- setNewTodoText("");
206
- refetch(); // クエリを再実行して最新データを取得
217
+ return {
218
+ status: 200,
219
+ jsonBody: {
220
+ message: `Hello, ${result.data.name}! This message is from Azure Functions.`,
221
+ timestamp: new Date().toISOString()
222
+ }
207
223
  };
224
+ }
208
225
 
209
- const handleToggleTodo = async (id: string) => {
210
- await callServerFn(toggleTodo, { id });
211
- refetch();
212
- };
226
+ app.http('greet', {
227
+ methods: ['GET', 'POST'],
228
+ authLevel: 'anonymous',
229
+ handler: greet
230
+ });
231
+ ```
213
232
 
214
- const handleDeleteTodo = async (id: string) => {
215
- await callServerFn(deleteTodo, { id });
216
- refetch();
217
- };
233
+ ### 7. Use Schemas in Client Components
218
234
 
219
- // ... レンダリング
220
- }
221
- ```
235
+ ```typescript
236
+ // components/AddTodoForm.tsx
237
+ 'use client'
222
238
 
223
- ## 📚 API リファレンス
239
+ import { useState } from 'react';
240
+ import { addTodo } from '@/lib/server/todos';
241
+ import { todoSchema } from '@/lib/models/todo';
224
242
 
225
- ### `useServerFn<TResult>(serverFn, args, options?)`
243
+ export function AddTodoForm() {
244
+ const [text, setText] = useState('');
226
245
 
227
- **パラメータ:**
228
- - `serverFn`: サーバー関数
229
- - `args`: 関数の引数の配列
230
- - `options?`: オプション設定
246
+ const handleSubmit = async (e: React.FormEvent) => {
247
+ e.preventDefault();
248
+
249
+ // Client-side validation with Zod
250
+ const result = todoSchema.pick({ text: true }).safeParse({ text });
251
+ if (!result.success) {
252
+ alert(result.error.errors[0].message);
253
+ return;
254
+ }
255
+
256
+ await addTodo(text);
257
+ setText('');
258
+ };
231
259
 
232
- **戻り値:**
233
- ```typescript
234
- {
235
- data: TResult | null;
236
- loading: boolean;
237
- error: Error | null;
238
- refetch: () => Promise<void>;
260
+ return (
261
+ <form onSubmit={handleSubmit}>
262
+ <input value={text} onChange={(e) => setText(e.target.value)} />
263
+ <button type="submit">Add Todo</button>
264
+ </form>
265
+ );
239
266
  }
240
267
  ```
241
268
 
242
- **使用例:**
243
- ```typescript
244
- const { data, loading, error, refetch } = useServerFn(getTodos, []);
245
- ```
246
-
247
- ### `callServerFn<TArgs, TResult>(serverFn, ...args)`
269
+ ### 8. Develop and Deploy
248
270
 
249
- **パラメータ:**
250
- - `serverFn`: サーバー関数
251
- - `...args`: 関数の引数
271
+ ```bash
272
+ # Start Next.js + Azure Functions + Cosmos DB with one command
273
+ npx swallowkit dev
252
274
 
253
- **戻り値:**
254
- - `Promise<TResult>`: サーバー関数の実行結果
275
+ # Build for production
276
+ npx swallowkit build
255
277
 
256
- **使用例:**
257
- ```typescript
258
- const result = await callServerFn(addTodo, { text: "新しいTodo" });
278
+ # Deploy to Azure
279
+ npx swallowkit deploy --swa-name my-app --resource-group my-rg
259
280
  ```
260
281
 
261
- ## 🔧 CLI コマンド
282
+ ## 🏗️ Architecture
262
283
 
263
- ### `swallowkit init <project-name>`
284
+ ```
285
+ ┌─────────────────────────────────────────────────────────┐
286
+ │ Next.js App │
287
+ │ ┌──────────────┐ ┌──────────────────────────┐ │
288
+ │ │ Frontend │ │ BFF API Routes │ │
289
+ │ │ Components │─────▶│ /api/* │ │
290
+ │ │ (Client) │ │ (Next.js API Routes) │ │
291
+ │ └──────────────┘ └──────────┬───────────────┘ │
292
+ │ │ │
293
+ │ │ HTTP │
294
+ └───────────────────────────────────┼──────────────────────┘
295
+
296
+ ┌───────────────▼─────────────────┐
297
+ │ Azure Functions (External) │
298
+ │ - HTTP Triggers │
299
+ │ - Business Logic │
300
+ │ - Zod Validation │
301
+ └───────────────┬─────────────────┘
302
+
303
+ ┌───────────────▼─────────────────┐
304
+ │ Azure Cosmos DB │
305
+ │ - NoSQL Database │
306
+ │ - Schema Validation │
307
+ └─────────────────────────────────┘
308
+ ```
264
309
 
265
- 新しい SwallowKit プロジェクトを作成します。
310
+ **Key Patterns:**
311
+ - **BFF (Backend For Frontend)**: Next.js API routes proxy requests to Azure Functions
312
+ - **Shared Schemas**: Zod schemas used in frontend, BFF, Functions, and database
313
+ - **Type Safety**: End-to-end TypeScript types derived from Zod schemas
314
+ - **Standalone Deployment**: Next.js deployed as standalone to Azure Static Web Apps
315
+ - **External Backend**: Azure Functions deployed independently
266
316
 
267
- ```bash
268
- npx swallowkit init my-app
269
- ```
317
+ ### Development & Deployment Flow
270
318
 
271
- ### `swallowkit dev`
319
+ ```
320
+ ┌─────────────────────────────────────────────────────────────┐
321
+ │ Development │
322
+ ├─────────────────────────────────────────────────────────────┤
323
+ │ Next.js App (localhost:3000) │
324
+ │ ├─ Frontend Components │
325
+ │ ├─ BFF API Routes (/api/*) │
326
+ │ └─ Server Actions (Cosmos DB) │
327
+ │ │
328
+ │ Azure Functions (localhost:7071) │
329
+ │ └─ HTTP Triggers (greet, etc.) │
330
+ │ │
331
+ │ Cosmos DB Emulator (localhost:8081) │
332
+ └─────────────────────────────────────────────────────────────┘
272
333
 
273
- 統合開発環境を起動します:
274
- - Cosmos DB の自動セットアップ
275
- - Azure Functions の自動ビルド
276
- - Vite + SWA CLI の統合サーバー起動
334
+ ↓ swallowkit build
277
335
 
278
- ```bash
279
- npx swallowkit dev
336
+ ┌─────────────────────────────────────────────────────────────┐
337
+ │ Production (Azure) │
338
+ ├─────────────────────────────────────────────────────────────┤
339
+ │ Azure Static Web Apps │
340
+ │ └─ Next.js (standalone mode) │
341
+ │ ├─ Server Components (SSR) │
342
+ │ ├─ Client Components │
343
+ │ └─ BFF API Routes │
344
+ │ │
345
+ │ Azure Functions (separate deployment) │
346
+ │ └─ Backend API (CRUD, business logic) │
347
+ │ │
348
+ │ Azure Cosmos DB │
349
+ │ └─ Data storage with Zod validation │
350
+ └─────────────────────────────────────────────────────────────┘
280
351
  ```
281
352
 
282
- **オプション:**
283
- - `--port <port>`: SWA CLI のポート (デフォルト: 4280)
284
- - `--api-port <port>`: Azure Functions のポート (デフォルト: 7071)
285
- - `--host <host>`: ホスト名 (デフォルト: localhost)
286
- - `--open`: ブラウザを自動で開く
353
+ ### Why This Approach?
287
354
 
288
- ### `swallowkit generate`
355
+ **Problem**: Managing type consistency across frontend, backend, and database is error-prone and time-consuming.
289
356
 
290
- `src/serverFns.ts` から Azure Functions API を生成します。
357
+ **Solution**: SwallowKit provides a unified Zod schema layer that ensures type safety and validation across your entire stack:
291
358
 
292
- ```bash
293
- npx swallowkit generate
359
+ ```
360
+ Zod Schema (Single Source of Truth)
361
+ ├─ Frontend: Client-side validation
362
+ ├─ Next.js: Server component validation
363
+ ├─ Backend: Azure Functions validation
364
+ └─ Database: Cosmos DB document validation
294
365
  ```
295
366
 
296
- **オプション:**
297
- - `--output <dir>`: 出力ディレクトリ (デフォルト: ./api)
298
- - `--force`: 既存ファイルを上書き
367
+ ## 🎯 Core Feature: Zod Schema Sharing
299
368
 
300
- ### `swallowkit setup`
369
+ ### Why Zod Schema Sharing?
301
370
 
302
- 開発環境の依存関係をチェックします:
303
- - Azure CLI
304
- - Azure Static Web Apps CLI
305
- - Cosmos DB Emulator
371
+ 1. **Single Source of Truth**: Define your data schema once with Zod
372
+ 2. **Frontend Validation**: Use the same schema for client-side form validation
373
+ 3. **Backend Validation**: Automatically validate inputs in Server Actions and API routes
374
+ 4. **Database Schema**: Type-safe Cosmos DB operations with runtime validation
375
+ 5. **No Code Duplication**: Share schemas between client, server, and database
306
376
 
307
- ```bash
308
- npx swallowkit setup
377
+ ### Basic Usage
378
+
379
+ ```typescript
380
+ // lib/schemas/user.ts - Shared schema definition
381
+ import { z } from 'zod';
382
+
383
+ export const UserSchema = z.object({
384
+ id: z.string(),
385
+ name: z.string().min(1),
386
+ email: z.string().email(),
387
+ createdAt: z.string().default(() => new Date().toISOString()),
388
+ });
389
+
390
+ export type User = z.infer<typeof UserSchema>;
309
391
  ```
310
392
 
311
- ## 🔧 設定ファイル
393
+ ```typescript
394
+ // lib/server/users.ts - Server-side repository
395
+ import { createRepository } from 'swallowkit';
396
+ import { UserSchema } from '../schemas/user';
312
397
 
313
- ## 🔧 設定ファイル
398
+ const userRepo = createRepository('users', UserSchema);
314
399
 
315
- ### `swallowkit.config.json`
400
+ export async function getUsers() {
401
+ return userRepo.findAll(); // Type-safe, validated
402
+ }
316
403
 
317
- ```json
318
- {
319
- "database": {
320
- "type": "cosmos",
321
- "endpoint": "http://localhost:8081",
322
- "key": "C2y6yDjf5/R+...",
323
- "databaseName": "swallowkit-db"
324
- },
325
- "api": {
326
- "endpoint": "/api/_swallowkit"
327
- },
328
- "functions": {
329
- "outputDir": "api"
330
- }
404
+ export async function createUser(data: { name: string; email: string }) {
405
+ return userRepo.create({
406
+ id: crypto.randomUUID(),
407
+ ...data,
408
+ }); // Validated by Zod before saving to Cosmos DB
331
409
  }
332
410
  ```
333
411
 
334
- **プロパティ:**
335
- - `database.type`: データベースタイプ (現在は `"cosmos"` のみ)
336
- - `database.endpoint`: Cosmos DB エンドポイント
337
- - `database.key`: Cosmos DB アクセスキー
338
- - `database.databaseName`: データベース名
339
- - `api.endpoint`: RPC エンドポイントのパス
340
- - `functions.outputDir`: Azure Functions の出力ディレクトリ
341
-
342
- ### 環境変数 (`api/local.settings.json`)
343
-
344
- ```json
345
- {
346
- "IsEncrypted": false,
347
- "Values": {
348
- "FUNCTIONS_WORKER_RUNTIME": "node",
349
- "FUNCTIONS_WORKER_RUNTIME_VERSION": "~22",
350
- "AzureWebJobsStorage": "",
351
- "COSMOS_ENDPOINT": "http://localhost:8081",
352
- "COSMOS_KEY": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
353
- },
354
- "Host": {
355
- "CORS": "*",
356
- "LocalHttpPort": 7071
412
+ ```typescript
413
+ // app/actions.ts - Server Actions with validation
414
+ 'use server'
415
+
416
+ import { UserSchema } from '@/lib/schemas/user';
417
+ import { createUser } from '@/lib/server/users';
418
+ import { revalidatePath } from 'next/cache';
419
+
420
+ export async function createUserAction(formData: FormData) {
421
+ // Validate input using the same schema
422
+ const result = UserSchema.pick({ name: true, email: true }).safeParse({
423
+ name: formData.get('name'),
424
+ email: formData.get('email'),
425
+ });
426
+
427
+ if (!result.success) {
428
+ return { error: result.error.message };
357
429
  }
430
+
431
+ await createUser(result.data);
432
+ revalidatePath('/users');
358
433
  }
359
434
  ```
360
435
 
361
- ## 🏗️ アーキテクチャ
362
-
363
- ### クライアント・サーバー分離
364
-
365
- ```
366
- ┌─────────────────────────────────────────────────────────────┐
367
- クライアント (Browser) │
368
- │ │
369
- │ src/App.tsx │
370
- │ ↓ import │
371
- src/serverFns.ts (型定義スタブ) │
372
- │ ↓ useServerFn / callServerFn │
373
- │ swallowkit (hooks/useServerFn.ts) │
374
- POST /api/_swallowkit │
375
- └──────────────────────────────────────────────────────────────┘
376
-
377
- │ HTTP Request
378
-
379
- ┌─────────────────────────────────────────────────────────────┐
380
- │ サーバー (Azure Functions v4)
381
- │ │
382
- │ api/src/functions/rpc.ts │
383
- ↓ import │
384
- │ api/src/shared/server-functions.ts (Cosmos DB実装)
385
- @azure/cosmos │
386
- Cosmos DB │
387
- └─────────────────────────────────────────────────────────────┘
436
+ ```typescript
437
+ // components/UserForm.tsx - Client-side validation
438
+ 'use client'
439
+
440
+ import { UserSchema } from '@/lib/schemas/user';
441
+ import { createUserAction } from '@/app/actions';
442
+ import { useState } from 'react';
443
+
444
+ export function UserForm() {
445
+ const [error, setError] = useState('');
446
+
447
+ const handleSubmit = async (formData: FormData) => {
448
+ // Client-side validation using the same schema
449
+ const result = UserSchema.pick({ name: true, email: true }).safeParse({
450
+ name: formData.get('name'),
451
+ email: formData.get('email'),
452
+ });
453
+
454
+ if (!result.success) {
455
+ setError(result.error.errors[0].message);
456
+ return;
457
+ }
458
+
459
+ setError('');
460
+ await createUserAction(formData);
461
+ };
462
+
463
+ return (
464
+ <form action={handleSubmit}>
465
+ <input name="name" required />
466
+ <input name="email" type="email" required />
467
+ {error && <p className="error">{error}</p>}
468
+ <button>Create User</button>
469
+ </form>
470
+ );
471
+ }
388
472
  ```
389
473
 
390
- ### データフロー
474
+ ## 🔧 Database Integration
391
475
 
392
- 1. **クエリ (useServerFn)**
393
- - コンポーネントマウント時に自動実行
394
- - ローディング状態を管理
395
- - データをキャッシュ
396
- - `refetch()` で再実行可能
476
+ ### Repository API
397
477
 
398
- 2. **ミューテーション (callServerFn)**
399
- - イベントハンドラから明示的に呼び出し
400
- - 状態管理なし
401
- - 完了後に `refetch()` を呼び出してクエリを更新
478
+ ```typescript
479
+ import { createRepository } from 'swallowkit';
480
+ import { z } from 'zod';
402
481
 
403
- ### Cosmos DB 自動セットアップ
482
+ const UserSchema = z.object({
483
+ id: z.string(),
484
+ name: z.string(),
485
+ email: z.string().email(),
486
+ });
404
487
 
405
- `swallowkit dev` コマンド実行時:
488
+ const userRepo = createRepository('users', UserSchema);
406
489
 
407
- ```typescript
408
- // dev コマンドの処理フロー
409
- 1. Cosmos DB Emulator の起動確認
410
- 2. CosmosClient で接続
411
- 3. database.createIfNotExists('swallowkit-db')
412
- 4. container.createIfNotExists('todos', {
413
- partitionKey: {
414
- paths: ['/id'],
415
- kind: PartitionKeyKind.Hash
416
- }
417
- })
418
- 5. Azure Functions API のビルド
419
- 6. Vite + SWA CLI の起動
420
- ```
490
+ // Create (with validation)
491
+ await userRepo.create({
492
+ id: '1',
493
+ name: 'John',
494
+ email: 'john@example.com'
495
+ });
421
496
 
422
- ## 📁 プロジェクト構造
497
+ // Find by ID
498
+ const user = await userRepo.findById('1');
423
499
 
424
- ```
425
- my-app/
426
- ├── src/
427
- │ ├── App.tsx # React アプリケーション
428
- │ ├── serverFns.ts # サーバー関数の型定義 (クライアント側)
429
- │ ├── index.tsx # エントリーポイント
430
- │ └── index.css # スタイル
431
- ├── api/ # Azure Functions (自動生成)
432
- │ ├── src/
433
- │ │ ├── functions/
434
- │ │ │ ├── rpc.ts # RPC エンドポイント
435
- │ │ │ └── crud.ts # CRUD エンドポイント (未使用)
436
- │ │ └── shared/
437
- │ │ └── server-functions.ts # サーバー側実装 (Cosmos DB)
438
- │ ├── host.json
439
- │ ├── local.settings.json
440
- │ ├── tsconfig.json
441
- │ └── package.json
442
- ├── swallowkit.config.json # SwallowKit 設定
443
- ├── staticwebapp.config.json # Azure SWA 設定
444
- ├── package.json
445
- └── vite.config.ts
446
- ```
500
+ // Find all
501
+ const users = await userRepo.findAll();
447
502
 
448
- ## 🚨 重要な注意事項
503
+ // Update (with validation)
504
+ await userRepo.update({
505
+ id: '1',
506
+ name: 'John Doe',
507
+ email: 'john@example.com'
508
+ });
449
509
 
450
- ### 1. サーバー関数の戻り値
510
+ // Delete
511
+ await userRepo.delete('1');
451
512
 
452
- **❌ NG: `Promise<void>`**
453
- ```typescript
454
- export async function deleteTodo({ id }: { id: string }): Promise<void> {
455
- await container.item(id, id).delete();
456
- // JSON レスポンスがないため RPC 呼び出しが失敗
457
- }
513
+ // Custom queries
514
+ const activeUsers = await userRepo.findWhere('c.isActive = true');
458
515
  ```
459
516
 
460
- **✅ OK: 必ず値を返す**
517
+ ### Advanced: Custom Repository
518
+
461
519
  ```typescript
462
- export async function deleteTodo({ id }: { id: string }): Promise<{ success: boolean }> {
463
- await container.item(id, id).delete();
464
- return { success: true }; // JSON レスポンスを返す
520
+ import { SchemaRepository } from 'swallowkit';
521
+ import { z } from 'zod';
522
+
523
+ const UserSchema = z.object({
524
+ id: z.string(),
525
+ email: z.string().email(),
526
+ isActive: z.boolean(),
527
+ });
528
+
529
+ class UserRepository extends SchemaRepository<z.infer<typeof UserSchema>> {
530
+ constructor() {
531
+ super('users', UserSchema);
532
+ }
533
+
534
+ async findActiveUsers() {
535
+ return this.findWhere('c.isActive = true');
536
+ }
537
+
538
+ async findByEmail(email: string) {
539
+ const users = await this.findWhere('c.email = @email', [email]);
540
+ return users[0] || null;
541
+ }
465
542
  }
543
+
544
+ export const userRepo = new UserRepository();
466
545
  ```
467
546
 
468
- ### 2. パーティションキーの指定
547
+ ### Cosmos DB Client
469
548
 
470
- Cosmos DB Emulator では `kind: PartitionKeyKind.Hash` の明示が必須:
549
+ For advanced use cases, you can use the Cosmos DB client directly:
471
550
 
472
551
  ```typescript
473
- await database.containers.createIfNotExists({
474
- id: 'todos',
475
- partitionKey: {
476
- paths: ['/id'],
477
- kind: PartitionKeyKind.Hash // 必須!
478
- }
479
- });
480
- ```
552
+ import { getDatabaseClient } from 'swallowkit';
481
553
 
482
- ### 3. クライアント・サーバーの分離
554
+ const client = getDatabaseClient();
483
555
 
484
- - `src/serverFns.ts` - **クライアント側の型定義のみ** (throw Error)
485
- - `api/src/shared/server-functions.ts` - **サーバー側の実装** (Cosmos DB アクセス)
556
+ // Direct operations
557
+ await client.createDocument('users', { id: '1', name: 'John' });
558
+ await client.getDocument('users', '1');
559
+ await client.updateDocument('users', { id: '1', name: 'John Doe' });
560
+ await client.deleteDocument('users', '1');
561
+ await client.query('users', 'SELECT * FROM c WHERE c.isActive = true');
562
+ ```
486
563
 
487
- この2つは **別ファイル** です。混同しないでください。
564
+ ## 📚 CLI Commands
488
565
 
489
- ### 4. generateコマンドの動作
566
+ ### `swallowkit init [project-name]`
490
567
 
491
- `swallowkit generate` は:
492
- - `src/serverFns.ts` がクライアントスタブ (throw Error を含む) の場合
493
- → デフォルトの Cosmos DB テンプレートを使用
494
- - `src/serverFns.ts` が既に実装を含む場合
495
- → その実装を `api/src/shared/server-functions.ts` にコピー
568
+ Initialize a new SwallowKit project with Next.js template.
496
569
 
497
- ## 🐛 トラブルシューティング
570
+ ```bash
571
+ npx swallowkit init my-app
572
+ ```
498
573
 
499
- ### Cosmos DB Emulator が起動しない
574
+ ### `swallowkit dev`
575
+
576
+ Start development server with Cosmos DB setup and Next.js.
500
577
 
501
- **Windows:**
502
578
  ```bash
503
- # スタートメニューから "Azure Cosmos DB Emulator" を起動
504
- # または、サービスから起動
579
+ npx swallowkit dev
505
580
  ```
506
581
 
507
- **Docker:**
582
+ Options:
583
+ - `-p, --port <port>`: Next.js port (default: `3000`)
584
+ - `--host <host>`: Host name (default: `localhost`)
585
+ - `--open`: Open browser automatically
586
+ - `--verbose`: Show detailed logs
587
+
588
+ ### `swallowkit build`
589
+
590
+ Build the Next.js app for production using standalone mode.
591
+
508
592
  ```bash
509
- docker run -p 8081:8081 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
593
+ npx swallowkit build
510
594
  ```
511
595
 
512
- ### パーティションキーエラー
596
+ Options:
597
+ - `--output <dir>`: Output directory (default: `dist`)
513
598
 
514
- `PartitionKeyKind.Hash` を指定してください:
599
+ ### `swallowkit deploy`
515
600
 
516
- ```typescript
517
- import { CosmosClient, PartitionKeyKind } from '@azure/cosmos';
601
+ Deploy to Azure Static Web Apps.
518
602
 
519
- await database.containers.createIfNotExists({
520
- id: 'your-container',
521
- partitionKey: {
522
- paths: ['/id'],
523
- kind: PartitionKeyKind.Hash
524
- }
525
- });
603
+ ```bash
604
+ npx swallowkit deploy --swa-name my-app --resource-group my-rg
526
605
  ```
527
606
 
528
- ### API が古い実装のまま
607
+ Options:
608
+ - `--swa-name <name>`: Azure Static Web Apps resource name (required)
609
+ - `--resource-group <name>`: Azure resource group (required)
610
+ - `--location <location>`: Azure region (default: `japaneast`)
611
+ - `--skip-build`: Skip build step
529
612
 
530
- `generate --force` で強制的に再生成:
613
+ ### `swallowkit setup`
614
+
615
+ Install required tools (Azure CLI, SWA CLI, Cosmos DB Emulator).
531
616
 
532
617
  ```bash
533
- npx swallowkit generate --force
534
- cd api
535
- npm run build
618
+ npx swallowkit setup
536
619
  ```
537
620
 
538
- ## 🎯 今後の予定 (TODO)
621
+ ## 🔧 Configuration
622
+
623
+ Create `swallowkit.config.js` in your project root:
624
+
625
+ ```javascript
626
+ module.exports = {
627
+ database: {
628
+ connectionString: process.env.COSMOS_DB_CONNECTION_STRING,
629
+ databaseName: 'MyAppDB',
630
+ },
631
+ api: {
632
+ endpoint: '/api/_swallowkit',
633
+ cors: {
634
+ origin: ['http://localhost:3000'],
635
+ credentials: true,
636
+ },
637
+ },
638
+ };
639
+ ```
539
640
 
540
- - [ ] Zod スキーマ統合
541
- - [ ] 認証・認可機能
542
- - [ ] ファイルアップロード機能
543
- - [ ] リアルタイム通信 (SignalR)
544
- - [ ] デプロイコマンド (`swallowkit deploy`)
545
- - [ ] テストユーティリティ
546
- - [ ] 本番環境用の設定管理
641
+ ## 🚀 Connecting to External Azure Functions
547
642
 
548
- ## 🤝 コントリビューション
643
+ SwallowKit is designed to work with external Azure Functions backends. Your Next.js app acts as a BFF (Backend for Frontend), while independent Azure Functions handle business logic and data operations.
549
644
 
550
- このプロジェクトは開発中です。フィードバックや提案を歓迎します!
645
+ ### Backend Connection Pattern
551
646
 
552
- ## 📄 ライセンス
647
+ ```typescript
648
+ // lib/api/backend.ts
649
+ const BACKEND_URL = process.env.BACKEND_API_URL || 'http://localhost:7071';
553
650
 
554
- MIT
651
+ export async function fetchFromBackend<T>(endpoint: string, options?: RequestInit): Promise<T> {
652
+ const response = await fetch(`${BACKEND_URL}${endpoint}`, options);
653
+ if (!response.ok) {
654
+ throw new Error(`Backend API error: ${response.statusText}`);
655
+ }
656
+ return response.json();
657
+ }
658
+ ```
659
+
660
+ ```typescript
661
+ // app/api/todos/route.ts - Next.js API Route (BFF)
662
+ import { fetchFromBackend } from '@/lib/api/backend';
663
+ import { TodoSchema } from '@/lib/schemas/todo';
664
+
665
+ export async function GET() {
666
+ const todos = await fetchFromBackend('/api/todos');
667
+
668
+ // Validate response using shared schema
669
+ const validated = z.array(TodoSchema).parse(todos);
670
+
671
+ return Response.json(validated);
672
+ }
673
+ ```
674
+
675
+ ## 🤝 Contributing
676
+
677
+ This project is in active development. Feedback and suggestions are welcome!
555
678
 
556
- ## 🔗 関連リンク
679
+ ## 📄 License
680
+
681
+ MIT
557
682
 
558
- - [Azure Static Web Apps](https://azure.microsoft.com/ja-jp/services/app-service/static/)
559
- - [Azure Functions](https://azure.microsoft.com/ja-jp/services/functions/)
560
- - [Azure Cosmos DB](https://azure.microsoft.com/ja-jp/services/cosmos-db/)
561
- - [React](https://reactjs.org/)
562
- - [Vite](https://vitejs.dev/)
683
+ ## 🔗 Related Links
563
684
 
685
+ - [Azure Static Web Apps](https://azure.microsoft.com/services/app-service/static/)
686
+ - [Azure Functions](https://azure.microsoft.com/services/functions/)
687
+ - [Azure Cosmos DB](https://azure.microsoft.com/services/cosmos-db/)
688
+ - [Next.js](https://nextjs.org/)
689
+ - [Zod](https://zod.dev/)
690
+ - [TypeScript](https://www.typescriptlang.org/)