@cougargrades/firebase-rest-firestore 1.6.0

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 ADDED
@@ -0,0 +1,393 @@
1
+ # Firebase REST Firestore
2
+
3
+ [日本語版はこちら(Japanese Version)](./README.ja.md)
4
+
5
+ Firebase Firestore REST API client for Edge runtime environments like Cloudflare Workers and Vercel Edge Functions.
6
+
7
+ ## Features
8
+
9
+ - Works in Edge runtime environments where Firebase Admin SDK is not available
10
+ - Full CRUD operations support
11
+ - TypeScript support
12
+ - Token caching for better performance
13
+ - Simple and intuitive API
14
+ - Explicit configuration without hidden environment variable dependencies
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install firebase-rest-firestore
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { createFirestoreClient } from "firebase-rest-firestore";
26
+
27
+ // Create a client with your configuration
28
+ const firestore = createFirestoreClient({
29
+ projectId: "your-project-id",
30
+ privateKey: "your-private-key",
31
+ clientEmail: "your-client-email",
32
+ });
33
+
34
+ // Add a document
35
+ const newDoc = await firestore.add("collection", {
36
+ name: "Test Document",
37
+ value: 100,
38
+ });
39
+
40
+ // Get a document
41
+ const doc = await firestore.get("collection", newDoc.id);
42
+
43
+ // Update a document
44
+ await firestore.update("collection", newDoc.id, { value: 200 });
45
+
46
+ // Query documents
47
+ const querySnapshot = await firestore
48
+ .collection("games")
49
+ .where("score", ">", 50)
50
+ .where("active", "==", true)
51
+ .orderBy("score", "desc")
52
+ .limit(10)
53
+ .get();
54
+
55
+ // Process query results
56
+ const games = [];
57
+ querySnapshot.forEach(doc => {
58
+ games.push({
59
+ id: doc.id,
60
+ ...doc.data(),
61
+ });
62
+ });
63
+ console.log("Games with score > 50:", games);
64
+
65
+ // Delete a document
66
+ await firestore.delete("collection", newDoc.id);
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ The `FirestoreConfig` object requires the following properties:
72
+
73
+ | Property | Description |
74
+ | ----------- | ---------------------------- |
75
+ | projectId | Firebase project ID |
76
+ | privateKey | Service account private key |
77
+ | clientEmail | Service account client email |
78
+
79
+ ## API Reference
80
+
81
+ ### FirestoreClient
82
+
83
+ The main class for interacting with Firestore.
84
+
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
92
+
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.
105
+
106
+ #### get(collectionName, documentId)
107
+
108
+ Retrieves a document by ID.
109
+
110
+ #### update(collectionName, documentId, data)
111
+
112
+ Updates an existing document.
113
+
114
+ #### delete(collectionName, documentId)
115
+
116
+ Deletes a document.
117
+
118
+ #### query(collectionName, options)
119
+
120
+ Queries documents in a collection with filtering, ordering, and pagination.
121
+
122
+ ### createFirestoreClient(config)
123
+
124
+ Creates a new FirestoreClient instance with the provided configuration.
125
+
126
+ #### add(collectionName, data)
127
+
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.
136
+
137
+ ## Error Handling
138
+
139
+ Firebase REST Firestore throws exceptions with appropriate error messages when API requests fail. Here's an example of error handling:
140
+
141
+ ```typescript
142
+ try {
143
+ // Try to get a document
144
+ const game = await firestore.get("games", "non-existent-id");
145
+
146
+ // If document doesn't exist, null is returned
147
+ if (game === null) {
148
+ console.log("Document not found");
149
+ return;
150
+ }
151
+
152
+ // Process document if it exists
153
+ console.log("Fetched game:", game);
154
+ } catch (error) {
155
+ // Handle API errors (authentication, network, etc.)
156
+ console.error("Firestore error:", error.message);
157
+ }
158
+ ```
159
+
160
+ Common error cases:
161
+
162
+ - Authentication errors (invalid credentials)
163
+ - Network errors
164
+ - Invalid query parameters
165
+ - Firestore rate limits
166
+
167
+ ## Query Options Details
168
+
169
+ The `query` method supports the following options for filtering, sorting, and paginating Firestore documents:
170
+
171
+ ### where
172
+
173
+ Specify multiple filter conditions. Each condition is an object with the following properties:
174
+
175
+ - `field`: The field name to filter on
176
+ - `op`: The comparison operator. Available values:
177
+ - `EQUAL`: Equal to
178
+ - `NOT_EQUAL`: Not equal to
179
+ - `LESS_THAN`: Less than
180
+ - `LESS_THAN_OR_EQUAL`: Less than or equal to
181
+ - `GREATER_THAN`: Greater than
182
+ - `GREATER_THAN_OR_EQUAL`: Greater than or equal to
183
+ - `ARRAY_CONTAINS`: Array contains
184
+ - `IN`: Equal to any of the specified values
185
+ - `ARRAY_CONTAINS_ANY`: Array contains any of the specified values
186
+ - `NOT_IN`: Not equal to any of the specified values
187
+ - `value`: The value to compare against
188
+
189
+ ```typescript
190
+ // Query games with score > 50 and active = true
191
+ const games = await firestore.query("games", {
192
+ where: [
193
+ { field: "score", op: "GREATER_THAN", value: 50 },
194
+ { field: "active", op: "EQUAL", value: true },
195
+ ],
196
+ });
197
+ ```
198
+
199
+ ### orderBy
200
+
201
+ Specifies the field name to sort results by. Results are sorted in ascending order by default.
202
+
203
+ ```typescript
204
+ // Sort by creation time
205
+ const games = await firestore.query("games", {
206
+ orderBy: "createdAt",
207
+ });
208
+ ```
209
+
210
+ ### limit
211
+
212
+ Limits the maximum number of results returned.
213
+
214
+ ```typescript
215
+ // Get at most 10 documents
216
+ const games = await firestore.query("games", {
217
+ limit: 10,
218
+ });
219
+ ```
220
+
221
+ ### offset
222
+
223
+ Specifies the number of results to skip. Useful for pagination.
224
+
225
+ ```typescript
226
+ // Skip the first 20 results and get the next 10
227
+ const games = await firestore.query("games", {
228
+ offset: 20,
229
+ limit: 10,
230
+ });
231
+ ```
232
+
233
+ Example of a compound query:
234
+
235
+ ```typescript
236
+ // Get top 10 active games by score
237
+ const topGames = await firestore.query("games", {
238
+ where: [{ field: "active", op: "EQUAL", value: true }],
239
+ orderBy: "score", // Sort by score
240
+ limit: 10,
241
+ });
242
+ ```
243
+
244
+ ## Edge Runtime Examples
245
+
246
+ ### Cloudflare Workers
247
+
248
+ ```typescript
249
+ // Set these environment variables in wrangler.toml
250
+ // FIREBASE_PROJECT_ID
251
+ // FIREBASE_PRIVATE_KEY
252
+ // FIREBASE_CLIENT_EMAIL
253
+
254
+ import { createFirestoreClient } from "firebase-rest-firestore";
255
+
256
+ export default {
257
+ async fetch(request, env, ctx) {
258
+ // Load configuration from environment variables
259
+ const firestore = createFirestoreClient({
260
+ projectId: env.FIREBASE_PROJECT_ID,
261
+ privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"),
262
+ clientEmail: env.FIREBASE_CLIENT_EMAIL,
263
+ });
264
+
265
+ const url = new URL(request.url);
266
+ const path = url.pathname;
267
+
268
+ // Example API endpoint
269
+ if (path === "/api/games" && request.method === "GET") {
270
+ try {
271
+ // Get active games
272
+ const games = await firestore.query("games", {
273
+ where: [{ field: "active", op: "EQUAL", value: true }],
274
+ limit: 10,
275
+ });
276
+
277
+ return new Response(JSON.stringify(games), {
278
+ headers: { "Content-Type": "application/json" },
279
+ });
280
+ } catch (error) {
281
+ return new Response(JSON.stringify({ error: error.message }), {
282
+ status: 500,
283
+ headers: { "Content-Type": "application/json" },
284
+ });
285
+ }
286
+ }
287
+
288
+ return new Response("Not found", { status: 404 });
289
+ },
290
+ };
291
+ ```
292
+
293
+ ### Vercel Edge Functions
294
+
295
+ ```typescript
296
+ // Set these environment variables in .env.local
297
+ // FIREBASE_PROJECT_ID
298
+ // FIREBASE_PRIVATE_KEY
299
+ // FIREBASE_CLIENT_EMAIL
300
+
301
+ import { createFirestoreClient } from "firebase-rest-firestore";
302
+
303
+ export const config = {
304
+ runtime: "edge",
305
+ };
306
+
307
+ export default async function handler(request) {
308
+ // Load configuration from environment variables
309
+ const firestore = createFirestoreClient({
310
+ projectId: process.env.FIREBASE_PROJECT_ID,
311
+ privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"),
312
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
313
+ });
314
+
315
+ try {
316
+ // Get the latest 10 documents
317
+ const documents = await firestore.query("posts", {
318
+ orderBy: "createdAt",
319
+ limit: 10,
320
+ });
321
+
322
+ return new Response(JSON.stringify(documents), {
323
+ headers: { "Content-Type": "application/json" },
324
+ });
325
+ } catch (error) {
326
+ return new Response(JSON.stringify({ error: error.message }), {
327
+ status: 500,
328
+ headers: { "Content-Type": "application/json" },
329
+ });
330
+ }
331
+ }
332
+ ```
333
+
334
+ ## Performance Considerations
335
+
336
+ ### Token Caching
337
+
338
+ Firebase REST Firestore caches JWT tokens to improve performance. By default, tokens are cached for 50 minutes (actual token expiry is 1 hour). This eliminates the need to generate a new token for each request, improving API request speed.
339
+
340
+ ```typescript
341
+ // Tokens are cached internally, so multiple requests
342
+ // have minimal authentication overhead
343
+ const doc1 = await firestore.get("collection", "doc1");
344
+ const doc2 = await firestore.get("collection", "doc2");
345
+ const doc3 = await firestore.get("collection", "doc3");
346
+ ```
347
+
348
+ ### Query Optimization
349
+
350
+ When dealing with large amounts of data, consider the following:
351
+
352
+ 1. **Set appropriate limits**: Always use the `limit` parameter to restrict the number of documents returned.
353
+
354
+ 2. **Query only needed fields**: Future versions will add support for retrieving only specific fields.
355
+
356
+ 3. **Create indexes**: For complex queries, create appropriate indexes in the Firebase console.
357
+
358
+ 4. **Use pagination**: When retrieving large datasets, implement pagination using `offset` and `limit`.
359
+
360
+ ### Edge Environment Considerations
361
+
362
+ In edge environments, be aware of:
363
+
364
+ 1. **Cold starts**: Initial execution has token generation overhead.
365
+
366
+ 2. **Memory usage**: Be mindful of memory limits when processing large amounts of data.
367
+
368
+ 3. **Timeouts**: Long-running queries may hit edge environment timeout limits.
369
+
370
+ ## Limitations and Roadmap
371
+
372
+ ### Current Limitations
373
+
374
+ - **Batch operations**: The current version does not support batch processing for operating on multiple documents at once.
375
+ - **Transactions**: Atomic transaction operations are not supported.
376
+ - **Real-time listeners**: Due to the nature of REST APIs, real-time data synchronization is not supported.
377
+ - **Subcollections**: The current version has limited direct support for nested subcollections.
378
+
379
+ ### Future Roadmap
380
+
381
+ The following features are planned for future versions:
382
+
383
+ - Batch operations support
384
+ - Basic transaction support
385
+ - Improved subcollection support
386
+ - More detailed query options (compound indexes, etc.)
387
+ - Performance optimizations
388
+
389
+ Please report feature requests and bugs via GitHub Issues.
390
+
391
+ ## License
392
+
393
+ MIT