@zeroin.earth/appwrite-graphql 23.0.1 → 23.0.2

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 CHANGED
@@ -1,34 +1,31 @@
1
1
  # Appwrite GraphQL
2
+
2
3
  ![Static Badge](https://img.shields.io/badge/coverage-95%25-brightgreen) ![NPM Version](https://img.shields.io/npm/v/%40zeroin.earth%2Fappwrite-graphql) ![Static Badge](https://img.shields.io/badge/appwrite-v1.8.1-%23FD366E)
3
4
 
4
5
  Appwrite is an open source, BaaS in the same vein as Supabase and Firebase, but geared more toward self-hosting.
5
6
 
6
7
  This is a fully featured GraphQL library built with [@tanstack/react-query](https://github.com/TanStack/query) on top of the Appwrite web SDK and is fully typed. Think of this library as the abstract wrapper you would have made yourself, but we already did it for you.
7
8
 
8
- ## Getting Started
9
- - [Installation](#installation)
10
- - [Basic Usage](#usage)
11
-
12
9
  ## Features
13
10
 
14
11
  - Dual build for both React and React Native
15
12
  - Full Appwrite SDK v23 parity using React hooks
16
13
  - [Optimistic Mutations](#optimistic-mutations)
17
- - Documents only
14
+ - Documents only
18
15
  - [Query Caching](#query-caching)
19
- - [QueryKey Builder](#querykey-builder)
16
+ - [QueryKey Builder](#querykey-builder)
20
17
  - [Offline-first Support](#offline-first-support)
21
- - [Built-in Offline Persisters](#built-in-offline-persisters) (localStorage, AsyncStorage)
22
- - [Custom Offline Persister Support](#custom-offline-persister-support)
23
- - [Conflict Resolution](#conflict-resolution)
18
+ - [Built-in Offline Persisters](#built-in-offline-persisters) (localStorage, AsyncStorage)
19
+ - [Custom Offline Persister Support](#custom-offline-persister-support)
20
+ - [Conflict Resolution](#conflict-resolution)
24
21
  - [SSR Support](#ssr-support)
25
22
  - [Field Selection](#field-selection)
26
23
  - [Suspense Queries](#suspense-queries)
27
- - Documents
28
- - Collections
24
+ - Documents
25
+ - Collections
29
26
  - [Pagination Hooks](#pagination-hooks)
30
- - Standard Pagination
31
- - Infinite Scroll
27
+ - Standard Pagination
28
+ - Infinite Scroll
32
29
  - [Appwrite QueryBuilder](#appwrite-querybuilder)
33
30
  - [React Query Devtools Support](#react-query-devtools-support)
34
31
 
@@ -48,9 +45,9 @@ bun add @zeroin.earth/appwrite-graphql
48
45
 
49
46
  **React Native:**
50
47
 
51
- - `@react-native-async-storage/async-storage`
52
- - `@react-native-community/netinfo`
53
- - `react-native-appwrite`
48
+ - `react-native-appwrite` - `^0.25.0`
49
+ - `@react-native-async-storage/async-storage` - `^3.0.1`
50
+ - `@react-native-community/netinfo` - `^12.0.1`
54
51
 
55
52
  ## Usage
56
53
 
@@ -61,10 +58,7 @@ The library is designed to use a single wrapper, `<AppwriteProvider>`. There are
61
58
  1. Basic (no offline-first support) - React
62
59
 
63
60
  ```tsx
64
- import {
65
- AppwriteProvider,
66
- createAppwriteClient
67
- } from '@zeroin.earth/appwrite-graphql'
61
+ import { AppwriteProvider, createAppwriteClient } from '@zeroin.earth/appwrite-graphql'
68
62
 
69
63
  const client = createAppwriteClient({
70
64
  endpoint: 'https://cloud.appwrite.io/v1',
@@ -72,11 +66,7 @@ const client = createAppwriteClient({
72
66
  })
73
67
 
74
68
  function App() {
75
- return (
76
- <AppwriteProvider client={client}>
77
- {/* your app */}
78
- </AppwriteProvider>
79
- )
69
+ return <AppwriteProvider client={client}>{/* your app */}</AppwriteProvider>
80
70
  }
81
71
  ```
82
72
 
@@ -114,14 +104,9 @@ function App() {
114
104
 
115
105
  ```tsx
116
106
  import AsyncStorage from '@react-native-async-storage/async-storage'
117
- import {
118
- AppwriteProvider,
119
- createOfflineClient,
120
- } from '@zeroin.earth/appwrite-graphql'
107
+ import { AppwriteProvider, createOfflineClient } from '@zeroin.earth/appwrite-graphql'
121
108
 
122
- import {
123
- reactNativeNetworkAdapter
124
- } from '@zeroin.earth/appwrite-graphql/react-native'
109
+ import { reactNativeNetworkAdapter } from '@zeroin.earth/appwrite-graphql/react-native'
125
110
 
126
111
  const { appwrite, queryClient, persister } = createOfflineClient({
127
112
  endpoint: 'https://cloud.appwrite.io/v1',
@@ -132,11 +117,7 @@ const { appwrite, queryClient, persister } = createOfflineClient({
132
117
 
133
118
  function App() {
134
119
  return (
135
- <AppwriteProvider
136
- client={appwrite}
137
- queryClient={queryClient}
138
- persister={persister}
139
- >
120
+ <AppwriteProvider client={appwrite} queryClient={queryClient} persister={persister}>
140
121
  {/* your app */}
141
122
  </AppwriteProvider>
142
123
  )
@@ -154,9 +135,15 @@ import {
154
135
  } from '@zeroin.earth/appwrite-graphql'
155
136
 
156
137
  const myPersister: Persister = {
157
- persistClient: async (client) => { /* write to your storage */ },
158
- restoreClient: async () => { /* read from your storage */ },
159
- removeClient: async () => { /* clear your storage */ },
138
+ persistClient: async (client) => {
139
+ /* write to your storage */
140
+ },
141
+ restoreClient: async () => {
142
+ /* read from your storage */
143
+ },
144
+ removeClient: async () => {
145
+ /* clear your storage */
146
+ },
160
147
  }
161
148
 
162
149
  const { appwrite, queryClient, persister } = createOfflineClient({
@@ -168,11 +155,7 @@ const { appwrite, queryClient, persister } = createOfflineClient({
168
155
 
169
156
  function App() {
170
157
  return (
171
- <AppwriteProvider
172
- client={appwrite}
173
- queryClient={queryClient}
174
- persister={persister}
175
- >
158
+ <AppwriteProvider client={appwrite} queryClient={queryClient} persister={persister}>
176
159
  {/* your app */}
177
160
  </AppwriteProvider>
178
161
  )
@@ -182,10 +165,7 @@ function App() {
182
165
  5. Offline - Imperative / non-React
183
166
 
184
167
  ```tsx
185
- import {
186
- createOfflineClient,
187
- webNetworkAdapter,
188
- } from '@zeroin.earth/appwrite-graphql'
168
+ import { createOfflineClient, webNetworkAdapter } from '@zeroin.earth/appwrite-graphql'
189
169
 
190
170
  const client = createOfflineClient({
191
171
  endpoint: 'https://cloud.appwrite.io/v1',
@@ -242,12 +222,9 @@ const person = useDocument<Person>(
242
222
  During development, we started getting annoyed with keeping our query keys straight, so we built a factory you can use to perform manual cache eviction. We tried to keep the pattern as close to Appwrite's `Channels` as possible.
243
223
 
244
224
  ```tsx
245
- import { Keys } from "@zeroin.earth/appwrite-graphql";
225
+ import { Keys } from '@zeroin.earth/appwrite-graphql'
246
226
 
247
- const queryKey = Keys.database(databaseId)
248
- .collection(collectionId)
249
- .document(documentId)
250
- .key()
227
+ const queryKey = Keys.database(databaseId).collection(collectionId).document(documentId).key()
251
228
  ```
252
229
 
253
230
  ## Offline-first Support
@@ -267,11 +244,11 @@ const { appwrite, queryClient, persister } = createOfflineClient({
267
244
  })
268
245
  ```
269
246
 
270
- The above example will serialize the mutations to localStorage after a set `throttleTime` elapses (defaults to 1000ms). Once the `networkAdpater` detects the device is online, the serialized mutations will instantly start replaying in order.
247
+ The above example will serialize the mutations to localStorage after a set `throttleTime` elapses (defaults to 1000ms). Once the `networkAdapter` detects the device is online, the serialized mutations will instantly start replaying in order.
271
248
 
272
249
  ### Custom Offline Persister Support
273
250
 
274
- Sometimes you want to bring your own persister, or just don't want to use localStorage or AsyncStorage. For this, you can build your own.
251
+ Sometimes you want to bring your own persister, or just don't want to use localStorage or AsyncStorage. For this, you can build your own. See [Provider example #4](#provider) above for a full example using a custom `Persister`.
275
252
 
276
253
  ### Conflict Resolution
277
254
 
@@ -283,7 +260,7 @@ createOfflineClient({
283
260
  projectId: 'my-project',
284
261
  storage: localStorage,
285
262
  networkAdapter: webNetworkAdapter(),
286
- conflictStrategy: 'last-write-wins'
263
+ conflictStrategy: 'last-write-wins',
287
264
  })
288
265
  ```
289
266
 
@@ -296,7 +273,8 @@ createOfflineClient({
296
273
  **custom**: A custom resolver function can be supplied with the following type:
297
274
 
298
275
  ```tsx
299
- conflictStrategy: ((context: ConflictContext) => Record<string, string | number | boolean | null> | 'abort')
276
+ conflictStrategy: (context: ConflictContext) =>
277
+ Record<string, string | number | boolean | null> | 'abort'
300
278
  ```
301
279
 
302
280
  - `abort` signals to drop the replaying mutation and change nothing.
@@ -310,47 +288,46 @@ We have exposed 3 of the most used queries Appwrite surfaces to be used in SSR p
310
288
  - `getCollectionQuery`
311
289
 
312
290
  ```tsx
313
- import * as React from "react";
314
- import {
315
- dehydrate,
316
- HydrationBoundary,
317
- QueryClient,
318
- } from "@tanstack/react-query";
291
+ import * as React from 'react'
292
+ import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
319
293
 
320
- import { createAppwriteClient, useCollection } from "./";
321
- import { getCollectionQuery } from "./";
294
+ import {
295
+ createAppwriteClient,
296
+ useCollection,
297
+ getCollectionQuery,
298
+ } from '@zeroin.earth/appwrite-graphql'
322
299
 
323
300
  type PostType = {
324
- title: string;
325
- image: string;
326
- description: string;
327
- };
301
+ title: string
302
+ image: string
303
+ description: string
304
+ }
328
305
 
329
306
  // This could also be getServerSideProps
330
307
  export async function getStaticProps() {
331
308
  const appwriteClient = createAppwriteClient({
332
- endpoint: "https://example.com/v1",
333
- projectId: "project-id",
334
- });
309
+ endpoint: 'https://example.com/v1',
310
+ projectId: 'project-id',
311
+ })
335
312
 
336
- const queryClient = new QueryClient();
313
+ const queryClient = new QueryClient()
337
314
 
338
315
  // Perform the prefetching of the collection query on the server.
339
316
  await queryClient.prefetchQuery(
340
317
  getCollectionQuery<PostType>(appwriteClient, {
341
- databaseId: "db1",
342
- collectionId: "col1",
343
- fields: ["title", "image", "description"],
318
+ databaseId: 'db1',
319
+ collectionId: 'col1',
320
+ fields: ['title', 'image', 'description'],
344
321
  }),
345
- );
322
+ )
346
323
 
347
- // Dehydrate the query client state and pass it as a
324
+ // Dehydrate the query client state and pass it as a
348
325
  // prop to the page component.
349
326
  return {
350
327
  props: {
351
328
  dehydratedState: dehydrate(queryClient),
352
329
  },
353
- };
330
+ }
354
331
  }
355
332
 
356
333
  function Posts() {
@@ -358,10 +335,10 @@ function Posts() {
358
335
  // cached data and not trigger a network request. If the cache is empty,
359
336
  // it will fetch the data from the Appwrite server normally.
360
337
  const { data } = useCollection<PostType>({
361
- databaseId: "db1",
362
- collectionId: "col1",
363
- fields: ["title", "image", "description"],
364
- });
338
+ databaseId: 'db1',
339
+ collectionId: 'col1',
340
+ fields: ['title', 'image', 'description'],
341
+ })
365
342
 
366
343
  return (
367
344
  <div>
@@ -373,7 +350,7 @@ function Posts() {
373
350
  </div>
374
351
  ))}
375
352
  </div>
376
- );
353
+ )
377
354
  }
378
355
 
379
356
  // The dehydrated state from the server is passed to the HydrationBoundary,
@@ -384,7 +361,7 @@ export default function PostsRoute({ dehydratedState }) {
384
361
  <HydrationBoundary state={dehydratedState}>
385
362
  <Posts />
386
363
  </HydrationBoundary>
387
- );
364
+ )
388
365
  }
389
366
  ```
390
367
 
@@ -399,18 +376,16 @@ The most used query hooks allow you to specify the fields returned by Appwrite t
399
376
 
400
377
  ```tsx
401
378
  type Person = {
402
- name: string;
403
- age: number;
404
- };
379
+ name: string
380
+ age: number
381
+ }
405
382
 
406
- const person = useDocument<Person>(
407
- {
408
- databaseId: "db1",
409
- collectionId: "col1",
410
- documentId: "doc1",
411
- fields: ["name", "age"],
412
- },
413
- );
383
+ const person = useDocument<Person>({
384
+ databaseId: 'db1',
385
+ collectionId: 'col1',
386
+ documentId: 'doc1',
387
+ fields: ['name', 'age'],
388
+ })
414
389
  ```
415
390
 
416
391
  ## Suspense Queries
@@ -430,34 +405,27 @@ We have included two pagination hooks out of the box
430
405
  **With Pagination:**
431
406
 
432
407
  ```tsx
433
- import * as React from "react";
434
- import { q, useCollectionWithPagination } from "./";
408
+ import * as React from 'react'
409
+ import { q, useCollectionWithPagination } from '@zeroin.earth/appwrite-graphql'
435
410
 
436
411
  type Item = {
437
- _id: string;
438
- name: string;
439
- };
412
+ _id: string
413
+ name: string
414
+ }
440
415
 
441
416
  export default function Test() {
442
- const {
443
- documents,
444
- page,
445
- total,
446
- nextPage,
447
- previousPage,
448
- hasNextPage,
449
- hasPreviousPage,
450
- } = useCollectionWithPagination<Item>({
451
- databaseId: "your-database-id",
452
- collectionId: "your-collection-id",
453
- queries: q<Item>()
454
- .equal("name", ["John", "Jane"])
455
- .createdBefore(new Date("2024-01-01").toDateString())
456
- .orderAsc("name")
457
- .build(),
458
- limit: 10,
459
- fields: ["name", "_id"],
460
- });
417
+ const { documents, page, total, nextPage, previousPage, hasNextPage, hasPreviousPage } =
418
+ useCollectionWithPagination<Item>({
419
+ databaseId: 'your-database-id',
420
+ collectionId: 'your-collection-id',
421
+ queries: q<Item>()
422
+ .equal('name', ['John', 'Jane'])
423
+ .createdBefore(new Date('2024-01-01').toDateString())
424
+ .orderAsc('name')
425
+ .build(),
426
+ limit: 10,
427
+ fields: ['name', '_id'],
428
+ })
461
429
 
462
430
  return (
463
431
  <div>
@@ -478,35 +446,33 @@ export default function Test() {
478
446
  <p>Page: {page}</p>
479
447
  <p>Total: {total}</p>
480
448
  </div>
481
- );
449
+ )
482
450
  }
483
451
  ```
484
452
 
485
453
  **Infinite Scroll**:
486
454
 
487
455
  ```tsx
488
- import * as React from "react";
489
- import { q, useInfiniteCollection } from "./";
456
+ import * as React from 'react'
457
+ import { q, useInfiniteCollection } from '@zeroin.earth/appwrite-graphql'
490
458
 
491
459
  type Item = {
492
- _id: string;
493
- name: string;
494
- };
460
+ _id: string
461
+ name: string
462
+ }
495
463
 
496
464
  export default function Test() {
497
- const { documents, fetchNextPage, hasNextPage } = useInfiniteCollection<Item>(
498
- {
499
- databaseId: "your-database-id",
500
- collectionId: "your-collection-id",
501
- queries: q<Item>()
502
- .equal("name", ["John", "Jane"])
503
- .createdBefore(new Date("2024-01-01").toDateString())
504
- .orderAsc("name")
505
- .build(),
506
- limit: 25,
507
- fields: ["name", "_id"],
508
- },
509
- );
465
+ const { documents, fetchNextPage, hasNextPage } = useInfiniteCollection<Item>({
466
+ databaseId: 'your-database-id',
467
+ collectionId: 'your-collection-id',
468
+ queries: q<Item>()
469
+ .equal('name', ['John', 'Jane'])
470
+ .createdBefore(new Date('2024-01-01').toDateString())
471
+ .orderAsc('name')
472
+ .build(),
473
+ limit: 25,
474
+ fields: ['name', '_id'],
475
+ })
510
476
 
511
477
  return (
512
478
  <div>
@@ -520,7 +486,7 @@ export default function Test() {
520
486
  Load More...
521
487
  </button>
522
488
  </div>
523
- );
489
+ )
524
490
  }
525
491
  ```
526
492
 
@@ -531,32 +497,32 @@ Appwrite SDK includes a built-in Query factory, but we wanted to make something
531
497
  Our QueryBuilder is type safe and exposes all underlying functions from the built-in version 1 for 1.
532
498
 
533
499
  ```tsx
534
- import { q } from "@zeroin.earth/appwrite-graphql";
500
+ import { q, useCollection } from '@zeroin.earth/appwrite-graphql'
535
501
 
536
502
  type YourType = {
537
- name: string;
538
- favNumber: number;
539
- favColor: string;
540
- favFood: string;
541
- };
503
+ name: string
504
+ favNumber: number
505
+ favColor: string
506
+ favFood: string
507
+ }
542
508
 
543
509
  export function Profiles() {
544
510
  const { documents, error, isLoading } = useCollection<YourType>({
545
- databaseId: "your-database-id",
546
- collectionId: "your-collection-id",
511
+ databaseId: 'your-database-id',
512
+ collectionId: 'your-collection-id',
547
513
  queries: q<YourType>()
548
514
  .or(
549
- (q) => q.equal("favColor", "blue").greaterThan("favNumber", 18),
550
- (q) => q.equal("favFood", "pizza").lessThan("favNumber", 10),
515
+ (q) => q.equal('favColor', 'blue').greaterThan('favNumber', 18),
516
+ (q) => q.equal('favFood', 'pizza').lessThan('favNumber', 10),
551
517
  (q) =>
552
518
  q.and(
553
- (q) => q.between("favNumber", 5, 15),
554
- (q) => q.startsWith("name", "A"),
519
+ (q) => q.between('favNumber', 5, 15),
520
+ (q) => q.startsWith('name', 'A'),
555
521
  ),
556
522
  )
557
523
 
558
524
  .build(),
559
- });
525
+ })
560
526
 
561
527
  return (
562
528
  <div>
@@ -566,14 +532,14 @@ export function Profiles() {
566
532
  <ul>
567
533
  {documents.map((doc) => (
568
534
  <li key={doc.$id}>
569
- Name: {doc.name}, Fav Number: {doc.favNumber}, Fav Color:{" "}
570
- {doc.favColor}, Fav Food: {doc.favFood}
535
+ Name: {doc.name}, Fav Number: {doc.favNumber}, Fav Color: {doc.favColor}, Fav Food:{' '}
536
+ {doc.favFood}
571
537
  </li>
572
538
  ))}
573
539
  </ul>
574
540
  )}
575
541
  </div>
576
- );
542
+ )
577
543
  }
578
544
  ```
579
545
 
@@ -583,44 +549,48 @@ React Query Devtools are bundled and ready to go. For any additional questions a
583
549
 
584
550
  ## Examples
585
551
 
552
+ ### Login with email or OAuth
553
+
586
554
  ```ts
587
- import { useLogin } from "@zeroin.earth/appwrite-graphql";
555
+ import { useLogin } from '@zeroin.earth/appwrite-graphql'
588
556
 
589
557
  export function LogIn() {
590
- const router = useRouter();
558
+ const router = useRouter()
591
559
 
592
- const { login, oAuthLogin } = useLogin();
560
+ const { login, oAuthLogin } = useLogin()
593
561
 
594
562
  const onSubmit: SubmitHandler<Inputs> = async (data) => {
595
563
  await login.mutateAsync(data, {
596
564
  onSuccess: () => {
597
- router.push("/profile");
565
+ router.push('/profile')
598
566
  },
599
- });
600
- };
567
+ })
568
+ }
601
569
 
602
570
  const loginWithGoogle = () => {
603
571
  oAuthLogin.mutate({
604
- provider: "google",
605
- success: "successUrl",
606
- failure: "failureUrl",
607
- });
608
- };
572
+ provider: 'google',
573
+ success: 'successUrl',
574
+ failure: 'failureUrl',
575
+ })
576
+ }
609
577
  }
610
578
  ```
611
579
 
612
580
  ---
613
581
 
582
+ ### Execute a server function
583
+
614
584
  ```ts
615
- import { useFunction } from "@zeroin.earth/appwrite-graphql";
585
+ import { useFunction } from '@zeroin.earth/appwrite-graphql'
616
586
 
617
587
  export function Form() {
618
- const { executeFunction } = useFunction();
588
+ const { executeFunction } = useFunction()
619
589
 
620
590
  const onSubmit: SubmitHandler<Input> = async (data) => {
621
591
  executeFunction.mutate(
622
592
  {
623
- functionId: "6gibhbyy6tggdf",
593
+ functionId: '6gibhbyy6tggdf',
624
594
  body: {
625
595
  message: {
626
596
  ...data,
@@ -629,10 +599,14 @@ export function Form() {
629
599
  },
630
600
  {
631
601
  onSettled: () => {
632
- setJustSignedUp(true);
602
+ setJustSignedUp(true)
633
603
  },
634
604
  },
635
- );
636
- };
605
+ )
606
+ }
637
607
  }
638
608
  ```
609
+
610
+ ## License
611
+
612
+ MIT