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.
- package/README.md +529 -402
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +127 -143
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/deploy.d.ts +3 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +147 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +114 -198
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -4
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +517 -291
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/index.js +11 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +3 -24
- package/dist/core/config.js.map +1 -1
- package/dist/database/base-model.d.ts +81 -0
- package/dist/database/base-model.d.ts.map +1 -0
- package/dist/database/base-model.js +120 -0
- package/dist/database/base-model.js.map +1 -0
- package/dist/database/client.d.ts +0 -4
- package/dist/database/client.d.ts.map +1 -1
- package/dist/database/client.js +10 -45
- package/dist/database/client.js.map +1 -1
- package/dist/database/runtime-check.d.ts +14 -0
- package/dist/database/runtime-check.d.ts.map +1 -0
- package/dist/database/runtime-check.js +26 -0
- package/dist/database/runtime-check.js.map +1 -0
- package/dist/index.d.ts +5 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -60
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +3 -24
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/api/rpc-handler.d.ts +0 -71
- package/dist/api/rpc-handler.d.ts.map +0 -1
- package/dist/api/rpc-handler.js +0 -205
- package/dist/api/rpc-handler.js.map +0 -1
- package/dist/cli/commands/generate.d.ts +0 -4
- package/dist/cli/commands/generate.d.ts.map +0 -1
- package/dist/cli/commands/generate.js +0 -238
- package/dist/cli/commands/generate.js.map +0 -1
- package/dist/generator/api-generator.d.ts +0 -53
- package/dist/generator/api-generator.d.ts.map +0 -1
- package/dist/generator/api-generator.js +0 -284
- package/dist/generator/api-generator.js.map +0 -1
- package/dist/generator/schema-parser.d.ts +0 -45
- package/dist/generator/schema-parser.d.ts.map +0 -1
- package/dist/generator/schema-parser.js +0 -198
- package/dist/generator/schema-parser.js.map +0 -1
- package/dist/generator/templates/azure-functions.d.ts +0 -15
- package/dist/generator/templates/azure-functions.d.ts.map +0 -1
- package/dist/generator/templates/azure-functions.js +0 -274
- package/dist/generator/templates/azure-functions.js.map +0 -1
- package/dist/generator/templates/default-server-functions.d.ts +0 -2
- package/dist/generator/templates/default-server-functions.d.ts.map +0 -1
- package/dist/generator/templates/default-server-functions.js +0 -67
- package/dist/generator/templates/default-server-functions.js.map +0 -1
- package/dist/hooks/server-function-registry.d.ts +0 -67
- package/dist/hooks/server-function-registry.d.ts.map +0 -1
- package/dist/hooks/server-function-registry.js +0 -153
- package/dist/hooks/server-function-registry.js.map +0 -1
- package/dist/hooks/useQuery.d.ts +0 -61
- package/dist/hooks/useQuery.d.ts.map +0 -1
- package/dist/hooks/useQuery.js +0 -147
- package/dist/hooks/useQuery.js.map +0 -1
- package/dist/hooks/useServerFn.d.ts +0 -27
- package/dist/hooks/useServerFn.d.ts.map +0 -1
- package/dist/hooks/useServerFn.js +0 -119
- package/dist/hooks/useServerFn.js.map +0 -1
- package/dist/schemas/example.d.ts +0 -657
- package/dist/schemas/example.d.ts.map +0 -1
- package/dist/schemas/example.js +0 -133
- package/dist/schemas/example.js.map +0 -1
- package/dist/server/todo-functions.d.ts +0 -21
- package/dist/server/todo-functions.d.ts.map +0 -1
- package/dist/server/todo-functions.js +0 -121
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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: [
|
|
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-
|
|
35
|
-
cd my-
|
|
40
|
+
npx swallowkit init my-app
|
|
41
|
+
cd my-app
|
|
36
42
|
npm install
|
|
37
43
|
```
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
60
|
+
Or via chocolatey (Windows):
|
|
54
61
|
```bash
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
+
```bash
|
|
74
|
+
npx swallowkit dev
|
|
75
|
+
```
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
The demo app will be available at:
|
|
83
|
+
- Next.js: http://localhost:3000
|
|
84
|
+
- Azure Functions: http://localhost:7071
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
**Options:**
|
|
87
|
+
```bash
|
|
88
|
+
# Customize ports
|
|
89
|
+
npx swallowkit dev --port 3001 --functions-port 7072
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
91
|
+
# Skip Azure Functions (Next.js only)
|
|
92
|
+
npx swallowkit dev --no-functions
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
# Open browser automatically
|
|
95
|
+
npx swallowkit dev --open
|
|
96
|
+
|
|
97
|
+
# Verbose output
|
|
98
|
+
npx swallowkit dev --verbose
|
|
94
99
|
```
|
|
95
100
|
|
|
96
|
-
###
|
|
101
|
+
### 4. Project Structure
|
|
97
102
|
|
|
98
|
-
```
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
+
Create type-safe schemas that will be used across your entire stack:
|
|
111
138
|
|
|
112
139
|
```typescript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
150
|
+
export type Todo = z.infer<typeof TodoSchema>;
|
|
151
|
+
```
|
|
122
152
|
|
|
123
|
-
|
|
124
|
-
id: string;
|
|
125
|
-
text: string;
|
|
126
|
-
completed: boolean;
|
|
127
|
-
}
|
|
153
|
+
### 5. Define Shared Zod Schemas
|
|
128
154
|
|
|
129
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
145
|
-
await container.item(id, id).delete();
|
|
146
|
-
return { success: true };
|
|
147
|
-
}
|
|
169
|
+
export type TodoType = z.infer<typeof todoSchema>;
|
|
148
170
|
|
|
149
|
-
export
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
178
|
+
### 6. Use BFF Pattern for Azure Functions
|
|
161
179
|
|
|
162
|
-
|
|
180
|
+
Next.js API routes act as a BFF (Backend For Frontend) layer that calls Azure Functions:
|
|
163
181
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
188
|
+
export async function GET(request: NextRequest) {
|
|
189
|
+
const { searchParams } = new URL(request.url);
|
|
190
|
+
const name = searchParams.get('name') || 'World';
|
|
175
191
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
const greetRequestSchema = z.object({
|
|
206
|
+
name: z.string().min(1).max(50),
|
|
207
|
+
});
|
|
196
208
|
|
|
197
|
-
function
|
|
198
|
-
const
|
|
199
|
-
const
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
app.http('greet', {
|
|
227
|
+
methods: ['GET', 'POST'],
|
|
228
|
+
authLevel: 'anonymous',
|
|
229
|
+
handler: greet
|
|
230
|
+
});
|
|
231
|
+
```
|
|
213
232
|
|
|
214
|
-
|
|
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
|
-
|
|
239
|
+
import { useState } from 'react';
|
|
240
|
+
import { addTodo } from '@/lib/server/todos';
|
|
241
|
+
import { todoSchema } from '@/lib/models/todo';
|
|
224
242
|
|
|
225
|
-
|
|
243
|
+
export function AddTodoForm() {
|
|
244
|
+
const [text, setText] = useState('');
|
|
226
245
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
-
|
|
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
|
-
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
251
|
-
|
|
271
|
+
```bash
|
|
272
|
+
# Start Next.js + Azure Functions + Cosmos DB with one command
|
|
273
|
+
npx swallowkit dev
|
|
252
274
|
|
|
253
|
-
|
|
254
|
-
|
|
275
|
+
# Build for production
|
|
276
|
+
npx swallowkit build
|
|
255
277
|
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
##
|
|
282
|
+
## 🏗️ Architecture
|
|
262
283
|
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
npx swallowkit init my-app
|
|
269
|
-
```
|
|
317
|
+
### Development & Deployment Flow
|
|
270
318
|
|
|
271
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
355
|
+
**Problem**: Managing type consistency across frontend, backend, and database is error-prone and time-consuming.
|
|
289
356
|
|
|
290
|
-
|
|
357
|
+
**Solution**: SwallowKit provides a unified Zod schema layer that ensures type safety and validation across your entire stack:
|
|
291
358
|
|
|
292
|
-
```
|
|
293
|
-
|
|
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
|
-
###
|
|
369
|
+
### Why Zod Schema Sharing?
|
|
301
370
|
|
|
302
|
-
|
|
303
|
-
-
|
|
304
|
-
|
|
305
|
-
- Cosmos DB
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
400
|
+
export async function getUsers() {
|
|
401
|
+
return userRepo.findAll(); // Type-safe, validated
|
|
402
|
+
}
|
|
316
403
|
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
393
|
-
- コンポーネントマウント時に自動実行
|
|
394
|
-
- ローディング状態を管理
|
|
395
|
-
- データをキャッシュ
|
|
396
|
-
- `refetch()` で再実行可能
|
|
476
|
+
### Repository API
|
|
397
477
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
- 完了後に `refetch()` を呼び出してクエリを更新
|
|
478
|
+
```typescript
|
|
479
|
+
import { createRepository } from 'swallowkit';
|
|
480
|
+
import { z } from 'zod';
|
|
402
481
|
|
|
403
|
-
|
|
482
|
+
const UserSchema = z.object({
|
|
483
|
+
id: z.string(),
|
|
484
|
+
name: z.string(),
|
|
485
|
+
email: z.string().email(),
|
|
486
|
+
});
|
|
404
487
|
|
|
405
|
-
|
|
488
|
+
const userRepo = createRepository('users', UserSchema);
|
|
406
489
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
1
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
|
|
510
|
+
// Delete
|
|
511
|
+
await userRepo.delete('1');
|
|
451
512
|
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
517
|
+
### Advanced: Custom Repository
|
|
518
|
+
|
|
461
519
|
```typescript
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
###
|
|
547
|
+
### Cosmos DB Client
|
|
469
548
|
|
|
470
|
-
Cosmos DB
|
|
549
|
+
For advanced use cases, you can use the Cosmos DB client directly:
|
|
471
550
|
|
|
472
551
|
```typescript
|
|
473
|
-
|
|
474
|
-
id: 'todos',
|
|
475
|
-
partitionKey: {
|
|
476
|
-
paths: ['/id'],
|
|
477
|
-
kind: PartitionKeyKind.Hash // 必須!
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
```
|
|
552
|
+
import { getDatabaseClient } from 'swallowkit';
|
|
481
553
|
|
|
482
|
-
|
|
554
|
+
const client = getDatabaseClient();
|
|
483
555
|
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
564
|
+
## 📚 CLI Commands
|
|
488
565
|
|
|
489
|
-
###
|
|
566
|
+
### `swallowkit init [project-name]`
|
|
490
567
|
|
|
491
|
-
|
|
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
|
-
###
|
|
574
|
+
### `swallowkit dev`
|
|
575
|
+
|
|
576
|
+
Start development server with Cosmos DB setup and Next.js.
|
|
500
577
|
|
|
501
|
-
**Windows:**
|
|
502
578
|
```bash
|
|
503
|
-
|
|
504
|
-
# または、サービスから起動
|
|
579
|
+
npx swallowkit dev
|
|
505
580
|
```
|
|
506
581
|
|
|
507
|
-
|
|
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
|
-
|
|
593
|
+
npx swallowkit build
|
|
510
594
|
```
|
|
511
595
|
|
|
512
|
-
|
|
596
|
+
Options:
|
|
597
|
+
- `--output <dir>`: Output directory (default: `dist`)
|
|
513
598
|
|
|
514
|
-
`
|
|
599
|
+
### `swallowkit deploy`
|
|
515
600
|
|
|
516
|
-
|
|
517
|
-
import { CosmosClient, PartitionKeyKind } from '@azure/cosmos';
|
|
601
|
+
Deploy to Azure Static Web Apps.
|
|
518
602
|
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
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
|
-
`
|
|
613
|
+
### `swallowkit setup`
|
|
614
|
+
|
|
615
|
+
Install required tools (Azure CLI, SWA CLI, Cosmos DB Emulator).
|
|
531
616
|
|
|
532
617
|
```bash
|
|
533
|
-
npx swallowkit
|
|
534
|
-
cd api
|
|
535
|
-
npm run build
|
|
618
|
+
npx swallowkit setup
|
|
536
619
|
```
|
|
537
620
|
|
|
538
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/)
|