firebase-rest-firestore 0.2.2 → 0.3.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.ja.md ADDED
@@ -0,0 +1,294 @@
1
+ # Firebase REST Firestore
2
+
3
+ Firebase Firestore REST API クライアント - Cloudflare Workers や Vercel Edge Functions などのエッジランタイム環境向け。
4
+
5
+ ## 特徴
6
+
7
+ - Firebase Admin SDK が利用できないエッジランタイム環境で動作
8
+ - 完全な CRUD 操作のサポート
9
+ - TypeScript サポート
10
+ - パフォーマンス向上のためのトークンキャッシング
11
+ - シンプルで直感的な API
12
+ - 環境変数への暗黙的な依存がない明示的な設定
13
+
14
+ ## インストール
15
+
16
+ ```bash
17
+ npm install firebase-rest-firestore
18
+ ```
19
+
20
+ ## 使用方法
21
+
22
+ ```typescript
23
+ import { initializeFirestore } from "firebase-rest-firestore";
24
+
25
+ // SDK互換クライアントを初期化
26
+ const db = initializeFirestore({
27
+ projectId: "your-project-id",
28
+ privateKey: "your-private-key",
29
+ clientEmail: "your-client-email",
30
+ });
31
+
32
+ // コレクションリファレンスの取得
33
+ const gamesRef = db.collection("games");
34
+
35
+ // ドキュメントの追加
36
+ const gameRef = await gamesRef.add({
37
+ name: "New Game",
38
+ createdAt: new Date(),
39
+ score: 100,
40
+ });
41
+
42
+ // ドキュメントの取得
43
+ const gameSnapshot = await gameRef.get();
44
+ console.log(gameSnapshot.data());
45
+
46
+ // ドキュメントの更新
47
+ await gameRef.update({
48
+ score: 200,
49
+ });
50
+
51
+ // ドキュメントの削除
52
+ await gameRef.delete();
53
+
54
+ // クエリの実行
55
+ const highScoreGames = await gamesRef
56
+ .where("score", ">", 150)
57
+ .where("createdAt", "<", new Date())
58
+ .get();
59
+
60
+ highScoreGames.forEach(doc => {
61
+ console.log(doc.id, "=>", doc.data());
62
+ });
63
+ ```
64
+
65
+ ## API リファレンス
66
+
67
+ ### createFirestoreClient(config)
68
+
69
+ Firestore クライアントを作成します。
70
+
71
+ #### パラメータ
72
+
73
+ - `config` (object): クライアント設定
74
+ - `projectId` (string): Firebase プロジェクト ID
75
+ - `privateKey` (string): サービスアカウントの秘密鍵
76
+ - `clientEmail` (string): サービスアカウントのメールアドレス
77
+
78
+ #### 戻り値
79
+
80
+ 以下のメソッドを持つ Firestore クライアントオブジェクト:
81
+
82
+ ### collection(collectionPath).add(data)
83
+
84
+ コレクション内に自動生成された ID を持つ新しいドキュメントを作成します。
85
+
86
+ #### パラメータ
87
+
88
+ - `data` (object): ドキュメントデータ
89
+
90
+ #### 戻り値
91
+
92
+ 作成されたドキュメントへの参照。
93
+
94
+ ### collection(collectionPath).doc(id?).set(data)
95
+
96
+ 指定された ID でドキュメントを作成または上書きします。ID が指定されていない場合は自動的に生成されます。
97
+
98
+ #### パラメータ
99
+
100
+ - `id` (string, オプション): ドキュメントの ID
101
+ - `data` (object): ドキュメントデータ
102
+
103
+ #### 戻り値
104
+
105
+ プロミス(作成または上書き操作の完了時に解決)。
106
+
107
+ ### client.get(collection, id)
108
+
109
+ ドキュメントを取得します。
110
+
111
+ #### パラメータ
112
+
113
+ - `collection` (string): ドキュメントが属するコレクション名
114
+ - `id` (string): 取得するドキュメントの ID
115
+
116
+ #### 戻り値
117
+
118
+ ドキュメントデータを含むオブジェクト。ドキュメントが存在しない場合は null。
119
+
120
+ ### client.update(collection, id, data)
121
+
122
+ 既存のドキュメントを更新します。
123
+
124
+ #### パラメータ
125
+
126
+ - `collection` (string): ドキュメントが属するコレクション名
127
+ - `id` (string): 更新するドキュメントの ID
128
+ - `data` (object): 更新するフィールドを含むオブジェクト
129
+
130
+ #### 戻り値
131
+
132
+ 更新されたドキュメントを表すオブジェクト。
133
+
134
+ ### client.delete(collection, id)
135
+
136
+ ドキュメントを削除します。
137
+
138
+ #### パラメータ
139
+
140
+ - `collection` (string): ドキュメントが属するコレクション名
141
+ - `id` (string): 削除するドキュメントの ID
142
+
143
+ #### 戻り値
144
+
145
+ 成功した場合は true。
146
+
147
+ ### client.query(collection, filters, options?)
148
+
149
+ コレクションに対してクエリを実行します。
150
+
151
+ #### パラメータ
152
+
153
+ - `collection` (string): クエリするコレクション名
154
+ - `filters` (array): 各フィルタは[フィールド, 演算子, 値]の形式の配列
155
+ - `options` (object, オプション):
156
+ - `orderBy` (array, オプション): 並べ替えの指定(例:[['score', 'desc'], ['createdAt', 'asc']])
157
+ - `limit` (number, オプション): 結果の最大数
158
+ - `offset` (number, オプション): スキップする結果の数
159
+ - `startAt` (any, オプション): この値から始まるドキュメントを返す
160
+ - `startAfter` (any, オプション): この値の後に始まるドキュメントを返す
161
+ - `endAt` (any, オプション): この値で終わるドキュメントを返す
162
+ - `endBefore` (any, オプション): この値の前に終わるドキュメントを返す
163
+
164
+ #### 戻り値
165
+
166
+ クエリ条件に一致するドキュメントの配列。
167
+
168
+ ## Next.js 環境での設定
169
+
170
+ Next.js アプリでは、サーバーサイドでのみ実行されるように設定してください:
171
+
172
+ ```typescript
173
+ // Initialize in a server component or API route
174
+ import { createFirestoreClient } from "firebase-rest-firestore";
175
+
176
+ export async function getServerSideProps() {
177
+ // Server-side only code
178
+ const firestore = createFirestoreClient({
179
+ projectId: process.env.FIREBASE_PROJECT_ID,
180
+ privateKey: process.env.FIREBASE_PRIVATE_KEY,
181
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
182
+ });
183
+
184
+ const data = await firestore.query("collection", [
185
+ /* your filters */
186
+ ]);
187
+
188
+ return {
189
+ props: {
190
+ data: JSON.parse(JSON.stringify(data)),
191
+ },
192
+ };
193
+ }
194
+ ```
195
+
196
+ ## Cloudflare Workers 環境での使用
197
+
198
+ ```typescript
199
+ import { createFirestoreClient } from "firebase-rest-firestore";
200
+
201
+ export default {
202
+ async fetch(request, env) {
203
+ const firestore = createFirestoreClient({
204
+ projectId: env.FIREBASE_PROJECT_ID,
205
+ privateKey: env.FIREBASE_PRIVATE_KEY,
206
+ clientEmail: env.FIREBASE_CLIENT_EMAIL,
207
+ });
208
+
209
+ // APIロジックの実装...
210
+ const data = await firestore.query("collection", [
211
+ /* your filters */
212
+ ]);
213
+
214
+ return new Response(JSON.stringify(data), {
215
+ headers: { "Content-Type": "application/json" },
216
+ });
217
+ },
218
+ };
219
+ ```
220
+
221
+ ## クイックスタート
222
+
223
+ ```typescript
224
+ import { createFirestoreClient } from "firebase-rest-firestore";
225
+
226
+ // 設定オブジェクトでクライアントを初期化
227
+ const firestore = createFirestoreClient({
228
+ projectId: "your-project-id",
229
+ privateKey: "your-private-key",
230
+ clientEmail: "your-client-email",
231
+ });
232
+
233
+ // ドキュメントの追加
234
+ const newDoc = await firestore.add("collection", {
235
+ name: "テストドキュメント",
236
+ value: 100,
237
+ });
238
+
239
+ // ドキュメントの取得
240
+ const doc = await firestore.get("collection", newDoc.id);
241
+
242
+ // ドキュメントの更新
243
+ await firestore.update("collection", newDoc.id, { value: 200 });
244
+
245
+ // ドキュメントのクエリ
246
+ const querySnapshot = await firestore
247
+ .collection("games")
248
+ .where("score", ">", 50)
249
+ .where("active", "==", true)
250
+ .orderBy("score", "desc")
251
+ .limit(10)
252
+ .get();
253
+
254
+ const games = [];
255
+ querySnapshot.forEach(doc => {
256
+ games.push({
257
+ id: doc.id,
258
+ ...doc.data(),
259
+ });
260
+ });
261
+ console.log("Games with score > 50:", games);
262
+
263
+ // ドキュメントの削除
264
+ await firestore.delete("collection", newDoc.id);
265
+ ```
266
+
267
+ ## 設定
268
+
269
+ Firestore の権限を持つ Firebase サービスアカウントが必要です:
270
+
271
+ ```typescript
272
+ createFirestoreClient({
273
+ projectId: "your-project-id",
274
+ privateKey: "your-private-key", // エスケープされた改行(\\n)を含む場合、自動的にフォーマットされます
275
+ clientEmail: "your-client-email",
276
+ });
277
+ ```
278
+
279
+ ## API リファレンス
280
+
281
+ ### add(collectionName, data)
282
+
283
+ コレクションに新しいドキュメントを追加します。
284
+
285
+ パラメータ:
286
+
287
+ - `collectionName`: コレクション名
288
+ - `data`: 追加するドキュメントデータ
289
+
290
+ 戻り値: 自動生成された ID を持つ追加されたドキュメント。
291
+
292
+ ## ライセンス
293
+
294
+ MIT
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Firebase REST Firestore
2
2
 
3
+ [日本語版はこちら(Japanese Version)](./README.ja.md)
4
+
3
5
  Firebase Firestore REST API client for Edge runtime environments like Cloudflare Workers and Vercel Edge Functions.
4
6
 
5
7
  ## Features
@@ -10,8 +12,6 @@ Firebase Firestore REST API client for Edge runtime environments like Cloudflare
10
12
  - Token caching for better performance
11
13
  - Simple and intuitive API
12
14
  - Explicit configuration without hidden environment variable dependencies
13
- - Firebase Admin SDK compatible interface (v0.2.0+)
14
- - Lazy configuration validation for Next.js compatibility (v0.2.1+)
15
15
 
16
16
  ## Installation
17
17
 
@@ -19,55 +19,7 @@ Firebase Firestore REST API client for Edge runtime environments like Cloudflare
19
19
  npm install firebase-rest-firestore
20
20
  ```
21
21
 
22
- ## Usage
23
-
24
- ### Basic usage with explicit configuration
25
-
26
- ```typescript
27
- import { createFirestoreClient } from "firebase-rest-firestore";
28
-
29
- // Create a client with your configuration
30
- const firestore = createFirestoreClient({
31
- projectId: "your-project-id",
32
- privateKey: "your-private-key",
33
- clientEmail: "your-client-email",
34
- });
35
-
36
- // Create a document
37
- const game = await firestore.create("games", {
38
- name: "New Game",
39
- createdAt: new Date(),
40
- score: 100,
41
- active: true,
42
- });
43
- console.log("Created game ID:", game.id);
44
-
45
- // Get a document
46
- const fetchedGame = await firestore.get("games", game.id);
47
- console.log("Fetched game:", fetchedGame);
48
-
49
- // Update a document
50
- const updatedGame = await firestore.update("games", game.id, {
51
- ...fetchedGame,
52
- name: "Updated Game Name",
53
- updatedAt: new Date(),
54
- });
55
-
56
- // Query documents
57
- const userGames = await firestore.query("games", {
58
- where: [{ field: "score", op: "GREATER_THAN", value: 50 }],
59
- orderBy: "createdAt",
60
- limit: 10,
61
- });
62
- console.log("Games with score > 50:", userGames);
63
-
64
- // Delete a document
65
- await firestore.delete("games", game.id);
66
- ```
67
-
68
- ### Firebase Admin SDK Compatible Interface (v0.2.0+)
69
-
70
- From version 0.2.0, firebase-rest-firestore provides a Firebase Admin SDK compatible interface:
22
+ ## Quick Start
71
23
 
72
24
  ```typescript
73
25
  import { createFirestoreClient } from "firebase-rest-firestore";
@@ -79,33 +31,17 @@ const firestore = createFirestoreClient({
79
31
  clientEmail: "your-client-email",
80
32
  });
81
33
 
82
- // Create a document with auto-generated ID
83
- const gameRef = firestore.collection("games").doc();
84
- await gameRef.set({
85
- name: "New Game",
86
- createdAt: new Date(),
87
- score: 100,
88
- active: true,
89
- });
90
- console.log("Created game ID:", gameRef.id);
91
-
92
- // Create a document with specific ID
93
- await firestore.collection("games").doc("game123").set({
94
- name: "Specific Game",
95
- createdAt: new Date(),
34
+ // Add a document
35
+ const newDoc = await firestore.add("collection", {
36
+ name: "Test Document",
37
+ value: 100,
96
38
  });
97
39
 
98
40
  // Get a document
99
- const gameDoc = await firestore.doc("games/game123").get();
100
- if (gameDoc.exists) {
101
- console.log("Fetched game:", gameDoc.data());
102
- }
41
+ const doc = await firestore.get("collection", newDoc.id);
103
42
 
104
43
  // Update a document
105
- await firestore.collection("games").doc("game123").update({
106
- name: "Updated Game Name",
107
- updatedAt: new Date(),
108
- });
44
+ await firestore.update("collection", newDoc.id, { value: 200 });
109
45
 
110
46
  // Query documents
111
47
  const querySnapshot = await firestore
@@ -127,19 +63,7 @@ querySnapshot.forEach(doc => {
127
63
  console.log("Games with score > 50:", games);
128
64
 
129
65
  // Delete a document
130
- await firestore.collection("games").doc("game123").delete();
131
-
132
- // Working with subcollections
133
- const commentRef = firestore
134
- .collection("games")
135
- .doc("game123")
136
- .collection("comments")
137
- .doc();
138
-
139
- await commentRef.set({
140
- text: "Great game!",
141
- createdAt: new Date(),
142
- });
66
+ await firestore.delete("collection", newDoc.id);
143
67
  ```
144
68
 
145
69
  ## Configuration
@@ -158,9 +82,26 @@ The `FirestoreConfig` object requires the following properties:
158
82
 
159
83
  The main class for interacting with Firestore.
160
84
 
161
- #### create(collectionName, data)
85
+ #### collection(collectionPath).add(data)
86
+
87
+ Creates a new document with an auto-generated ID in the specified collection.
88
+
89
+ Parameters:
90
+
91
+ - `data`: Document data to be added
162
92
 
163
- Creates a new document in the specified collection.
93
+ Returns: A reference to the created document.
94
+
95
+ #### collection(collectionPath).doc(id?).set(data)
96
+
97
+ Creates or overwrites a document with the specified ID. If no ID is provided, one will be auto-generated.
98
+
99
+ Parameters:
100
+
101
+ - `id` (optional): Document ID
102
+ - `data`: Document data
103
+
104
+ Returns: A promise that resolves when the set operation is complete.
164
105
 
165
106
  #### get(collectionName, documentId)
166
107
 
@@ -182,9 +123,16 @@ Queries documents in a collection with filtering, ordering, and pagination.
182
123
 
183
124
  Creates a new FirestoreClient instance with the provided configuration.
184
125
 
185
- ### loadConfigFromEnv()
126
+ #### add(collectionName, data)
186
127
 
187
- Helper function to load configuration from environment variables.
128
+ Adds a new document to the specified collection.
129
+
130
+ Parameters:
131
+
132
+ - `collectionName`: Name of the collection
133
+ - `data`: Document data to be added
134
+
135
+ Returns: The added document with auto-generated ID.
188
136
 
189
137
  ## Error Handling
190
138
 
@@ -443,451 +391,3 @@ Please report feature requests and bugs via GitHub Issues.
443
391
  ## License
444
392
 
445
393
  MIT
446
-
447
- ---
448
-
449
- # Firebase REST Firestore (日本語ドキュメント)
450
-
451
- Firebase Firestore REST API クライアントは、Cloudflare Workers や Vercel Edge Functions などのエッジランタイム環境向けに設計されています。
452
-
453
- ## 特徴
454
-
455
- - Firebase Admin SDK が利用できないエッジランタイム環境で動作
456
- - 完全な CRUD 操作のサポート
457
- - TypeScript サポート
458
- - パフォーマンス向上のためのトークンキャッシュ
459
- - シンプルで直感的な API
460
- - 環境変数に依存しない明示的な設定
461
- - Firebase Admin SDK compatible interface (v0.2.0+)
462
- - Next.js 互換性のための遅延設定検証 (v0.2.1+)
463
-
464
- ## インストール
465
-
466
- ```bash
467
- npm install firebase-rest-firestore
468
- ```
469
-
470
- ## 使い方
471
-
472
- ### 基本的な使い方(明示的な設定)
473
-
474
- ```typescript
475
- import { createFirestoreClient } from "firebase-rest-firestore";
476
-
477
- // 設定を指定してクライアントを作成
478
- const firestore = createFirestoreClient({
479
- projectId: "あなたのプロジェクトID",
480
- privateKey: "サービスアカウントの秘密鍵",
481
- clientEmail: "サービスアカウントのメールアドレス",
482
- });
483
-
484
- // ドキュメントの作成
485
- const game = await firestore.create("games", {
486
- name: "新しいゲーム",
487
- createdAt: new Date(),
488
- score: 100,
489
- active: true,
490
- });
491
- console.log("作成されたゲームID:", game.id);
492
-
493
- // ドキュメントの取得
494
- const fetchedGame = await firestore.get("games", game.id);
495
- console.log("取得したゲーム:", fetchedGame);
496
-
497
- // ドキュメントの更新
498
- const updatedGame = await firestore.update("games", game.id, {
499
- ...fetchedGame,
500
- name: "更新されたゲーム名",
501
- updatedAt: new Date(),
502
- });
503
-
504
- // ドキュメントのクエリ
505
- const userGames = await firestore.query("games", {
506
- where: [{ field: "score", op: "GREATER_THAN", value: 50 }],
507
- orderBy: "createdAt",
508
- limit: 10,
509
- });
510
- console.log("スコアが50より大きいゲーム:", userGames);
511
-
512
- // ドキュメントの削除
513
- await firestore.delete("games", game.id);
514
- ```
515
-
516
- ### Firebase Admin SDK 互換インターフェース (v0.2.0+)
517
-
518
- バージョン 0.2.0 から、firebase-rest-firestore は Firebase Admin SDK と互換性のあるインターフェースを提供しています:
519
-
520
- ```typescript
521
- import { createFirestoreClient } from "firebase-rest-firestore";
522
-
523
- // 設定を指定してクライアントを作成
524
- const firestore = createFirestoreClient({
525
- projectId: "あなたのプロジェクトID",
526
- privateKey: "サービスアカウントの秘密鍵",
527
- clientEmail: "サービスアカウントのメールアドレス",
528
- });
529
-
530
- // 自動生成IDでドキュメントを作成
531
- const gameRef = firestore.collection("games").doc();
532
- await gameRef.set({
533
- name: "新しいゲーム",
534
- createdAt: new Date(),
535
- score: 100,
536
- active: true,
537
- });
538
- console.log("作成されたゲームID:", gameRef.id);
539
-
540
- // 特定のIDでドキュメントを作成
541
- await firestore.collection("games").doc("game123").set({
542
- name: "特定のゲーム",
543
- createdAt: new Date(),
544
- });
545
-
546
- // ドキュメントの取得
547
- const gameDoc = await firestore.doc("games/game123").get();
548
- if (gameDoc.exists) {
549
- console.log("取得したゲーム:", gameDoc.data());
550
- }
551
-
552
- // ドキュメントの更新
553
- await firestore.collection("games").doc("game123").update({
554
- name: "更新されたゲーム名",
555
- updatedAt: new Date(),
556
- });
557
-
558
- // ドキュメントのクエリ
559
- const querySnapshot = await firestore
560
- .collection("games")
561
- .where("score", ">", 50)
562
- .where("active", "==", true)
563
- .orderBy("score", "desc")
564
- .limit(10)
565
- .get();
566
-
567
- // クエリ結果の処理
568
- const games = [];
569
- querySnapshot.forEach(doc => {
570
- games.push({
571
- id: doc.id,
572
- ...doc.data(),
573
- });
574
- });
575
- console.log("スコアが50より大きいゲーム:", games);
576
-
577
- // ドキュメントの削除
578
- await firestore.collection("games").doc("game123").delete();
579
-
580
- // サブコレクションの操作
581
- const commentRef = firestore
582
- .collection("games")
583
- .doc("game123")
584
- .collection("comments")
585
- .doc();
586
-
587
- await commentRef.set({
588
- text: "素晴らしいゲーム!",
589
- createdAt: new Date(),
590
- });
591
- ```
592
-
593
- ## 設定
594
-
595
- `FirestoreConfig`オブジェクトには以下のプロパティが必要です:
596
-
597
- | プロパティ | 説明 |
598
- | ----------- | ---------------------------------- |
599
- | projectId | Firebase プロジェクト ID |
600
- | privateKey | サービスアカウントの秘密鍵 |
601
- | clientEmail | サービスアカウントのメールアドレス |
602
-
603
- ## API リファレンス
604
-
605
- ### FirestoreClient
606
-
607
- Firestore と対話するためのメインクラスです。
608
-
609
- #### create(collectionName, data)
610
-
611
- 指定されたコレクションに新しいドキュメントを作成します。
612
-
613
- #### get(collectionName, documentId)
614
-
615
- ID によってドキュメントを取得します。
616
-
617
- #### update(collectionName, documentId, data)
618
-
619
- 既存のドキュメントを更新します。
620
-
621
- #### delete(collectionName, documentId)
622
-
623
- ドキュメントを削除します。
624
-
625
- #### query(collectionName, options)
626
-
627
- フィルタリング、並べ替え、ページネーションを使用してコレクション内のドキュメントをクエリします。
628
-
629
- ### createFirestoreClient(config)
630
-
631
- 提供された設定で新しい FirestoreClient インスタンスを作成します。
632
-
633
- ### loadConfigFromEnv()
634
-
635
- 環境変数から設定を読み込むためのヘルパー関数です。
636
-
637
- ## エラーハンドリング
638
-
639
- Firebase REST Firestore は、API リクエスト中にエラーが発生した場合、適切なエラーメッセージを含む例外をスローします。以下はエラーハンドリングの例です:
640
-
641
- ```typescript
642
- try {
643
- // ドキュメントの取得を試みる
644
- const game = await firestore.get("games", "non-existent-id");
645
-
646
- // ドキュメントが存在しない場合はnullが返される
647
- if (game === null) {
648
- console.log("ドキュメントが見つかりませんでした");
649
- return;
650
- }
651
-
652
- // ドキュメントが存在する場合の処理
653
- console.log("取得したゲーム:", game);
654
- } catch (error) {
655
- // API呼び出し中のエラー(認証エラーやネットワークエラーなど)
656
- console.error("Firestoreエラー:", error.message);
657
- }
658
- ```
659
-
660
- 一般的なエラーケース:
661
-
662
- - 認証エラー(無効なクレデンシャル)
663
- - ネットワークエラー
664
- - 無効なクエリパラメータ
665
- - Firestore のレート制限
666
-
667
- ## クエリオプションの詳細
668
-
669
- `query`メソッドでは、以下のオプションを使用して Firestore のドキュメントをフィルタリング、ソート、ページネーションできます:
670
-
671
- ### where
672
-
673
- 複数のフィルター条件を指定できます。各条件は以下のプロパティを持つオブジェクトです:
674
-
675
- - `field`: フィルタリングするフィールド名
676
- - `op`: 比較演算子。以下の値が使用可能です:
677
- - `EQUAL`: 等しい
678
- - `NOT_EQUAL`: 等しくない
679
- - `LESS_THAN`: より小さい
680
- - `LESS_THAN_OR_EQUAL`: 以下
681
- - `GREATER_THAN`: より大きい
682
- - `GREATER_THAN_OR_EQUAL`: 以上
683
- - `ARRAY_CONTAINS`: 配列に含まれる
684
- - `IN`: 指定した値のいずれかに等しい
685
- - `ARRAY_CONTAINS_ANY`: 配列が指定した値のいずれかを含む
686
- - `NOT_IN`: 指定した値のいずれにも等しくない
687
- - `value`: 比較する値
688
-
689
- ```typescript
690
- // スコアが50より大きく、activeがtrueのゲームを検索
691
- const games = await firestore.query("games", {
692
- where: [
693
- { field: "score", op: "GREATER_THAN", value: 50 },
694
- { field: "active", op: "EQUAL", value: true },
695
- ],
696
- });
697
- ```
698
-
699
- ### orderBy
700
-
701
- 結果を並べ替えるフィールド名を指定します。デフォルトでは昇順(ASCENDING)でソートされます。
702
-
703
- ```typescript
704
- // 作成日時で並べ替え
705
- const games = await firestore.query("games", {
706
- orderBy: "createdAt",
707
- });
708
- ```
709
-
710
- ### limit
711
-
712
- 返される結果の最大数を指定します。
713
-
714
- ```typescript
715
- // 最大10件のドキュメントを取得
716
- const games = await firestore.query("games", {
717
- limit: 10,
718
- });
719
- ```
720
-
721
- ### offset
722
-
723
- 結果のスキップ数を指定します。ページネーションに使用できます。
724
-
725
- ```typescript
726
- // 最初の20件をスキップして、次の10件を取得
727
- const games = await firestore.query("games", {
728
- offset: 20,
729
- limit: 10,
730
- });
731
- ```
732
-
733
- 複合クエリの例:
734
-
735
- ```typescript
736
- // アクティブなゲームをスコアの高い順に10件取得
737
- const topGames = await firestore.query("games", {
738
- where: [{ field: "active", op: "EQUAL", value: true }],
739
- orderBy: "score", // スコアでソート
740
- limit: 10,
741
- });
742
- ```
743
-
744
- ## エッジランタイムでの使用例
745
-
746
- ### Cloudflare Workers
747
-
748
- ```typescript
749
- // wrangler.toml に以下の環境変数を設定してください
750
- // FIREBASE_PROJECT_ID
751
- // FIREBASE_PRIVATE_KEY
752
- // FIREBASE_CLIENT_EMAIL
753
-
754
- import { createFirestoreClient } from "firebase-rest-firestore";
755
-
756
- export default {
757
- async fetch(request, env, ctx) {
758
- // 環境変数から設定を読み込む
759
- const firestore = createFirestoreClient({
760
- projectId: env.FIREBASE_PROJECT_ID,
761
- privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"),
762
- clientEmail: env.FIREBASE_CLIENT_EMAIL,
763
- });
764
-
765
- const url = new URL(request.url);
766
- const path = url.pathname;
767
-
768
- // APIエンドポイントの例
769
- if (path === "/api/games" && request.method === "GET") {
770
- try {
771
- // アクティブなゲームを取得
772
- const games = await firestore.query("games", {
773
- where: [{ field: "active", op: "EQUAL", value: true }],
774
- limit: 10,
775
- });
776
-
777
- return new Response(JSON.stringify(games), {
778
- headers: { "Content-Type": "application/json" },
779
- });
780
- } catch (error) {
781
- return new Response(JSON.stringify({ error: error.message }), {
782
- status: 500,
783
- headers: { "Content-Type": "application/json" },
784
- });
785
- }
786
- }
787
-
788
- return new Response("Not found", { status: 404 });
789
- },
790
- };
791
- ```
792
-
793
- ### Vercel Edge Functions
794
-
795
- ```typescript
796
- // .env.local に以下の環境変数を設定してください
797
- // FIREBASE_PROJECT_ID
798
- // FIREBASE_PRIVATE_KEY
799
- // FIREBASE_CLIENT_EMAIL
800
-
801
- import { createFirestoreClient } from "firebase-rest-firestore";
802
-
803
- export const config = {
804
- runtime: "edge",
805
- };
806
-
807
- export default async function handler(request) {
808
- // 環境変数から設定を読み込む
809
- const firestore = createFirestoreClient({
810
- projectId: process.env.FIREBASE_PROJECT_ID,
811
- privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"),
812
- clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
813
- });
814
-
815
- try {
816
- // 最新の10件のドキュメントを取得
817
- const documents = await firestore.query("posts", {
818
- orderBy: "createdAt",
819
- limit: 10,
820
- });
821
-
822
- return new Response(JSON.stringify(documents), {
823
- headers: { "Content-Type": "application/json" },
824
- });
825
- } catch (error) {
826
- return new Response(JSON.stringify({ error: error.message }), {
827
- status: 500,
828
- headers: { "Content-Type": "application/json" },
829
- });
830
- }
831
- }
832
- ```
833
-
834
- ## パフォーマンスに関する注意点
835
-
836
- ### トークンキャッシュ
837
-
838
- Firebase REST Firestore は、パフォーマンスを向上させるために JWT トークンをキャッシュします。デフォルトでは、トークンは 50 分間キャッシュされます(実際のトークン有効期限は 1 時間)。これにより、リクエストごとに新しいトークンを生成する必要がなくなり、API リクエストの速度が向上します。
839
-
840
- ```typescript
841
- // トークンは内部的にキャッシュされるため、
842
- // 複数のリクエストでも認証のオーバーヘッドは最小限に抑えられます
843
- const doc1 = await firestore.get("collection", "doc1");
844
- const doc2 = await firestore.get("collection", "doc2");
845
- const doc3 = await firestore.get("collection", "doc3");
846
- ```
847
-
848
- ### クエリの最適化
849
-
850
- 大量のデータを扱う場合は、以下の点に注意してください:
851
-
852
- 1. **適切な制限を設定する**: 常に`limit`パラメータを使用して、返されるドキュメント数を制限してください。
853
-
854
- 2. **必要なフィールドのみをクエリする**: 将来のバージョンでは、特定のフィールドのみを取得する機能が追加される予定です。
855
-
856
- 3. **インデックスの作成**: 複雑なクエリを実行する場合は、Firebase コンソールで適切なインデックスを作成してください。
857
-
858
- 4. **ページネーションの使用**: 大量のデータを取得する場合は、`offset`と`limit`を組み合わせてページネーションを実装してください。
859
-
860
- ### エッジ環境での注意点
861
-
862
- エッジ環境では、以下の点に注意してください:
863
-
864
- 1. **コールドスタート**: 初回実行時にはトークン生成のオーバーヘッドがあります。
865
-
866
- 2. **メモリ使用量**: 大量のデータを一度に処理する場合は、メモリ制限に注意してください。
867
-
868
- 3. **タイムアウト**: 長時間実行されるクエリは、エッジ環境のタイムアウト制限に達する可能性があります。
869
-
870
- ## 制限事項とロードマップ
871
-
872
- ### 現在の制限事項
873
-
874
- - **バッチ操作**: 現在のバージョンでは、複数のドキュメントを一度に操作するバッチ処理はサポートされていません。
875
- - **トランザクション**: 原子的なトランザクション操作はサポートされていません。
876
- - **リアルタイムリスナー**: REST API の性質上、リアルタイムのデータ同期はサポートされていません。
877
- - **サブコレクション**: 現在のバージョンでは、ネストされたサブコレクションの直接的なサポートは限定的です。
878
-
879
- ### 将来のロードマップ
880
-
881
- 以下の機能は将来のバージョンで実装予定です:
882
-
883
- - バッチ操作のサポート
884
- - 基本的なトランザクションのサポート
885
- - サブコレクションの改善されたサポート
886
- - より詳細なクエリオプション(複合インデックスなど)
887
- - パフォーマンス最適化
888
-
889
- 機能リクエストやバグ報告は、GitHub の Issue でお知らせください。
890
-
891
- ## ライセンス
892
-
893
- MIT
package/dist/client.d.ts CHANGED
@@ -34,12 +34,12 @@ export declare class FirestoreClient {
34
34
  */
35
35
  doc(path: string): DocumentReference;
36
36
  /**
37
- * Firestoreにドキュメントを作成
37
+ * Firestoreにドキュメントを追加
38
38
  * @param collectionName コレクション名
39
- * @param data 作成するデータ
40
- * @returns 作成されたドキュメント
39
+ * @param data 追加するデータ
40
+ * @returns 追加されたドキュメント
41
41
  */
42
- create(collectionName: string, data: Record<string, any>): Promise<Record<string, any> & {
42
+ add(collectionName: string, data: Record<string, any>): Promise<Record<string, any> & {
43
43
  id: string;
44
44
  }>;
45
45
  /**
package/dist/client.js CHANGED
@@ -5,6 +5,7 @@ exports.createFirestoreClient = createFirestoreClient;
5
5
  const auth_1 = require("./utils/auth");
6
6
  const converter_1 = require("./utils/converter");
7
7
  const path_1 = require("./utils/path");
8
+ const config_1 = require("./utils/config");
8
9
  /**
9
10
  * Firestoreクライアントクラス
10
11
  */
@@ -81,12 +82,12 @@ class FirestoreClient {
81
82
  return new DocumentReference(this, collectionPath, docId);
82
83
  }
83
84
  /**
84
- * Firestoreにドキュメントを作成
85
+ * Firestoreにドキュメントを追加
85
86
  * @param collectionName コレクション名
86
- * @param data 作成するデータ
87
- * @returns 作成されたドキュメント
87
+ * @param data 追加するデータ
88
+ * @returns 追加されたドキュメント
88
89
  */
89
- async create(collectionName, data) {
90
+ async add(collectionName, data) {
90
91
  // 操作前に設定をチェック
91
92
  this.checkConfig();
92
93
  const url = (0, path_1.getFirestoreBasePath)(this.config.projectId, collectionName);
@@ -190,25 +191,42 @@ class FirestoreClient {
190
191
  async query(collectionName, options = {}) {
191
192
  // 操作前に設定をチェック
192
193
  this.checkConfig();
193
- const url = `${(0, path_1.getFirestoreBasePath)(this.config.projectId, collectionName)}:runQuery`;
194
+ // 修正: URLの形式を修正
195
+ // :runQueryはコレクション単位ではなく、ドキュメントルート単位で実行
196
+ const basePath = `https://firestore.googleapis.com/v1/projects/${this.config.projectId}/databases/(default)/documents`;
197
+ const url = `${basePath}:runQuery`;
194
198
  // クエリ構築
195
199
  const structuredQuery = {
196
200
  from: [{ collectionId: collectionName }],
197
201
  };
198
202
  // フィルター条件
199
203
  if (options.where && options.where.length > 0) {
200
- structuredQuery.where = {
201
- compositeFilter: {
202
- op: "AND",
203
- filters: options.where.map(condition => ({
204
- fieldFilter: {
205
- field: { fieldPath: condition.field },
206
- op: condition.op,
207
- value: (0, converter_1.convertToFirestoreValue)(condition.value),
208
- },
209
- })),
210
- },
211
- };
204
+ // シンプルなケース: 1つの条件の場合
205
+ if (options.where.length === 1) {
206
+ const condition = options.where[0];
207
+ structuredQuery.where = {
208
+ fieldFilter: {
209
+ field: { fieldPath: condition.field },
210
+ op: condition.op,
211
+ value: (0, converter_1.convertToFirestoreValue)(condition.value),
212
+ },
213
+ };
214
+ }
215
+ else {
216
+ // 複数条件の場合
217
+ structuredQuery.where = {
218
+ compositeFilter: {
219
+ op: "AND",
220
+ filters: options.where.map(condition => ({
221
+ fieldFilter: {
222
+ field: { fieldPath: condition.field },
223
+ op: condition.op,
224
+ value: (0, converter_1.convertToFirestoreValue)(condition.value),
225
+ },
226
+ })),
227
+ },
228
+ };
229
+ }
212
230
  }
213
231
  // 並べ替え
214
232
  if (options.orderBy) {
@@ -228,23 +246,37 @@ class FirestoreClient {
228
246
  structuredQuery.offset = options.offset;
229
247
  }
230
248
  const token = await this.getToken();
231
- const response = await fetch(url, {
232
- method: "POST",
233
- headers: {
234
- "Content-Type": "application/json",
235
- Authorization: `Bearer ${token}`,
236
- },
237
- body: JSON.stringify({
238
- structuredQuery,
239
- }),
240
- });
241
- if (!response.ok) {
242
- throw new Error(`Firestore API error: ${response.statusText}`);
249
+ // 修正: structuredQueryをラップしたリクエストボディを作成
250
+ const requestBody = {
251
+ structuredQuery: structuredQuery,
252
+ };
253
+ console.log("クエリリクエスト:", JSON.stringify(requestBody, null, 2));
254
+ try {
255
+ const response = await fetch(url, {
256
+ method: "POST",
257
+ headers: {
258
+ "Content-Type": "application/json",
259
+ Authorization: `Bearer ${token}`,
260
+ },
261
+ body: JSON.stringify(requestBody),
262
+ });
263
+ const responseText = await response.text();
264
+ console.log("API レスポンス:", responseText);
265
+ if (!response.ok) {
266
+ throw new Error(`Firestore API error: ${response.statusText} - ${responseText}`);
267
+ }
268
+ const results = JSON.parse(responseText);
269
+ console.log("変換前の結果:", results);
270
+ const convertedResults = results
271
+ .filter(item => item.document)
272
+ .map(item => (0, converter_1.convertFromFirestoreDocument)(item.document));
273
+ console.log("変換後の結果:", convertedResults);
274
+ return convertedResults;
275
+ }
276
+ catch (error) {
277
+ console.error("クエリ実行エラー:", error);
278
+ throw error;
243
279
  }
244
- const results = (await response.json());
245
- return results
246
- .filter(item => item.document)
247
- .map(item => (0, converter_1.convertFromFirestoreDocument)(item.document));
248
280
  }
249
281
  }
250
282
  exports.FirestoreClient = FirestoreClient;
@@ -274,7 +306,7 @@ class CollectionReference {
274
306
  * @returns 作成されたドキュメントのリファレンス
275
307
  */
276
308
  async add(data) {
277
- const result = await this.client.create(this.path, data);
309
+ const result = await this.client.add(this.path, data);
278
310
  const docId = result.id;
279
311
  return new DocumentReference(this.client, this.path, docId);
280
312
  }
@@ -448,7 +480,7 @@ class DocumentReference {
448
480
  else {
449
481
  // 新規作成
450
482
  const newData = { ...data, id: this.docId };
451
- await this.client.create(this.collectionPath, newData);
483
+ await this.client.add(this.collectionPath, newData);
452
484
  }
453
485
  return new WriteResult();
454
486
  }
@@ -664,5 +696,12 @@ exports.WriteResult = WriteResult;
664
696
  * @returns FirestoreClientインスタンス
665
697
  */
666
698
  function createFirestoreClient(config) {
699
+ // 秘密鍵のフォーマットを確認
700
+ if (config.privateKey) {
701
+ config = {
702
+ ...config,
703
+ privateKey: (0, config_1.formatPrivateKey)(config.privateKey),
704
+ };
705
+ }
667
706
  return new FirestoreClient(config);
668
707
  }
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentRe
3
3
  export { getFirestoreToken } from "./utils/auth";
4
4
  export { convertToFirestoreValue, convertFromFirestoreValue, convertToFirestoreDocument, convertFromFirestoreDocument, } from "./utils/converter";
5
5
  export { getFirestoreBasePath, getDocumentId } from "./utils/path";
6
+ export { formatPrivateKey, formatConfig } from "./utils/config";
6
7
  export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.DocumentReference = exports.CollectionReference = exports.createFirestoreClient = exports.FirestoreClient = exports.getDocumentId = exports.getFirestoreBasePath = exports.convertFromFirestoreDocument = exports.convertToFirestoreDocument = exports.convertFromFirestoreValue = exports.convertToFirestoreValue = exports.getFirestoreToken = void 0;
17
+ exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.DocumentReference = exports.CollectionReference = exports.createFirestoreClient = exports.FirestoreClient = exports.formatConfig = exports.formatPrivateKey = exports.getDocumentId = exports.getFirestoreBasePath = exports.convertFromFirestoreDocument = exports.convertToFirestoreDocument = exports.convertFromFirestoreValue = exports.convertToFirestoreValue = exports.getFirestoreToken = void 0;
18
18
  // 型定義のエクスポート
19
19
  __exportStar(require("./types"), exports);
20
20
  // クライアントのエクスポート
@@ -38,3 +38,6 @@ Object.defineProperty(exports, "convertFromFirestoreDocument", { enumerable: tru
38
38
  var path_1 = require("./utils/path");
39
39
  Object.defineProperty(exports, "getFirestoreBasePath", { enumerable: true, get: function () { return path_1.getFirestoreBasePath; } });
40
40
  Object.defineProperty(exports, "getDocumentId", { enumerable: true, get: function () { return path_1.getDocumentId; } });
41
+ var config_1 = require("./utils/config");
42
+ Object.defineProperty(exports, "formatPrivateKey", { enumerable: true, get: function () { return config_1.formatPrivateKey; } });
43
+ Object.defineProperty(exports, "formatConfig", { enumerable: true, get: function () { return config_1.formatConfig; } });
@@ -0,0 +1,13 @@
1
+ import { FirestoreConfig } from "../types";
2
+ /**
3
+ * 秘密鍵の文字列内にある改行コードのエスケープシーケンスを実際の改行に変換する
4
+ * @param privateKey 変換する秘密鍵文字列
5
+ * @returns 変換後の秘密鍵文字列
6
+ */
7
+ export declare function formatPrivateKey(privateKey: string): string;
8
+ /**
9
+ * FirestoreConfigオブジェクトの秘密鍵をフォーマットする
10
+ * @param config 元のconfigオブジェクト
11
+ * @returns 秘密鍵をフォーマットしたconfigオブジェクト
12
+ */
13
+ export declare function formatConfig(config: FirestoreConfig): FirestoreConfig;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatPrivateKey = formatPrivateKey;
4
+ exports.formatConfig = formatConfig;
5
+ /**
6
+ * 秘密鍵の文字列内にある改行コードのエスケープシーケンスを実際の改行に変換する
7
+ * @param privateKey 変換する秘密鍵文字列
8
+ * @returns 変換後の秘密鍵文字列
9
+ */
10
+ function formatPrivateKey(privateKey) {
11
+ if (privateKey.includes("\\n")) {
12
+ return privateKey.replace(/\\n/g, "\n");
13
+ }
14
+ return privateKey;
15
+ }
16
+ /**
17
+ * FirestoreConfigオブジェクトの秘密鍵をフォーマットする
18
+ * @param config 元のconfigオブジェクト
19
+ * @returns 秘密鍵をフォーマットしたconfigオブジェクト
20
+ */
21
+ function formatConfig(config) {
22
+ return {
23
+ ...config,
24
+ privateKey: formatPrivateKey(config.privateKey),
25
+ };
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-rest-firestore",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Firebase Firestore REST API client for Edge runtime environments",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "scripts": {
12
12
  "build": "tsc",
13
13
  "prepublishOnly": "npm run build",
14
- "test": "echo \"Error: no test specified\" && exit 1"
14
+ "test": "vitest"
15
15
  },
16
16
  "keywords": [
17
17
  "firebase",
@@ -30,7 +30,9 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^18.16.0",
33
- "typescript": "^5.0.4"
33
+ "dotenv": "^16.4.7",
34
+ "typescript": "^5.0.4",
35
+ "vitest": "^3.0.9"
34
36
  },
35
37
  "engines": {
36
38
  "node": ">=16.0.0"