Skip to content

Commit 3edc877

Browse files
committed
UPdated readme docs, added missed cache/loader calls
1 parent 385c761 commit 3edc877

2 files changed

Lines changed: 115 additions & 7 deletions

File tree

README.md

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,117 @@
22

33
This is a CosmosDB DataSource for the Apollo GraphQL Server. It was adapted from the [MongoDB Data Source project](https://github.com/GraphQLGuide/apollo-datasource-mongodb).
44

5-
65
## Usage
76

8-
Use by creating a new class, inheriting from ```CosmsosDBDataSource``` passing in the CosmosDb container instance (created from the CosmosDB Javascript API)
7+
Use by creating a new class, inheriting from `CosmsosDBDataSource` passing in the CosmosDb container instance (created from the CosmosDB Javascript API)
8+
9+
`data-sources/Users.ts`
10+
11+
```typescript
12+
export class UserDataSource extends CosmosDataSource<UserDoc, ApolloContext> {}
13+
```
14+
15+
`server.ts`
16+
17+
```typescript
18+
import { CosmosClient } from "@azure/cosmos";
19+
20+
const cosmosClient = new CosmosClient({
21+
endpoint: "https://my-cosmos-db.documents.azure.com:443/",
22+
key: "--------key-goes-here---==",
23+
});
24+
const cosmosContainer = cosmosClient.database("MyDatabase").container("Items");
25+
26+
import UserDataSource from "./data-sources/Users.js";
27+
28+
const server = new ApolloServer({
29+
typeDefs,
30+
resolvers,
31+
dataSources: () => ({
32+
users: new UserDataSource(cosmosContainer),
33+
}),
34+
});
35+
```
36+
37+
## Custom Queries
38+
39+
CosmosDataSource exposes a `findManyByQuery` method that accepts a ComosDB SQL query either as a string or a `SqlQuerySpec` object containing the query and a parameter collection. This can be used direclty in the resolvers, but probably better to create wrappers that hide the query details:
40+
41+
```typescript
42+
export class UserDataSource extends CosmosDataSource<UserDoc, ApolloContext> {
43+
findManyByGroupID = async (group_id, args: FindArgs = {}) => {
44+
log.debug(`UserDataSource: getGroupUsers ${group_id}`);
45+
const query = `SELECT * FROM c where c.type = "User" and exists(select * from g in c.groups where g = @group_id) `;
46+
const results = await this.findManyByQuery(
47+
{
48+
query,
49+
parameters: [{ name: "@group_id", value: group_id }],
50+
},
51+
args
52+
);
53+
log.debug(`Result count ${results.resources.length}`);
54+
return results.resources;
55+
};
56+
findOneByUserName = async (userName: string, args: FindArgs = {}) => {
57+
const results = await this.findManyByQuery(
58+
{
59+
query: `SELECT * FROM c WHERE c.userName = @userName AND c.type = 'User'`,
60+
parameters: [{ name: "@userName", value: userName }],
61+
},
62+
args
63+
);
64+
if (results.resources && results.resources.length)
65+
return results.resources[0];
66+
return undefined;
67+
};
68+
}
69+
```
70+
71+
## Write Operations
972

10-
```data-sources/Users.ts```
73+
This DataSource has some built in mutation methods to create, update and delete items. They can be used directly in resolvers or wrapped with custom methods.
1174

12-
``` typescript
75+
```typescript
76+
await context.dataSources.users.createOne(userDoc);
1377

78+
await context.dataSources.uploads.deleteOne(uploadKey);
1479
```
1580

16-
```server.ts```
81+
The data loader (and cache, if used) are updated after mutation operations.
1782

18-
``` typescript
83+
## Batching
1984

20-
```
85+
Batching is provided on all queries using the DataLoader library.
86+
87+
## Caching
88+
89+
Caching is available on an opt-in basis by passing a `ttl` option on queries.
90+
91+
## Typescript
92+
93+
This library is written in Typescript and exports full type definitions, but usable in pure Javascript as well.
94+
95+
# API
96+
97+
```typescript
98+
99+
const thisUser = await users.findOneById(id: string, {ttl}) // => Promise<T>
100+
101+
const userPair = await users.findManyByIds([id1, id2], {ttl}) // => Promise<(T | undefined)[]>
102+
103+
await users.deleteFromCacheById(id}) // => Promise<void>
104+
105+
// query method; note that this returns the CosmosDB FeedResponse object because sometimes this extra information is useful
106+
const userListResponse = await users.findManyByQuery('SELECT * from c where c.type="User"', {ttl, requestOptions}) // => Promise<FeedResponse<T>>
107+
console.log(userListResponse.resources) // user array from query
108+
109+
// create method returns the CosmosDB ItemResponse object
110+
const createResponse = await users.createOne(newUser) // => Promise<ItemResponse<T>>
111+
console.log(createResponse.resource) // user object returned from create, with CosmosDB-added values
112+
113+
const updateUserResponse = await users.updateOne(thisUser) // => Promise<ItemResponse<T>>
114+
115+
const updatePartialResponse = await users.updateOnePartial(id, {firstName: "Bob"}) // => Promise<ItemResponse<T>>
116+
console.log(updatePartialResponse.resource) // full user object from DB after updates
117+
118+
```

src/datasource.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export class CosmosDataSource<TData extends { id: string }, TContext = any>
6969
newDoc,
7070
requestOptions
7171
);
72+
if (response.resource) {
73+
this.primeLoader(response.resource, options.ttl);
74+
}
7275
return response;
7376
}
7477

@@ -77,13 +80,17 @@ export class CosmosDataSource<TData extends { id: string }, TContext = any>
7780
`CosmosDataSource/deleteOne: deleting id: '${id}'`
7881
);
7982
const response = await this.container.item(id, id).delete<TData>();
83+
await this.deleteFromCacheById(id);
8084
return response;
8185
}
8286

8387
async updateOne(updDoc: TData) {
8488
const response = await this.container
8589
.item(updDoc.id, updDoc.id)
8690
.replace(updDoc);
91+
if (response.resource) {
92+
this.primeLoader(response.resource);
93+
}
8794
return response;
8895
}
8996

@@ -96,6 +103,9 @@ export class CosmosDataSource<TData extends { id: string }, TContext = any>
96103
const { resource } = docItem;
97104
const newResource = { ...resource, ...contents, id } as TData; // don't change the ID ever
98105
const replaceResult = await item.replace<TData>(newResource);
106+
if (replaceResult.resource) {
107+
this.primeLoader(replaceResult.resource);
108+
}
99109

100110
return replaceResult;
101111
}

0 commit comments

Comments
 (0)