Skip to content

Commit 8c7936b

Browse files
committed
Initial version
0 parents  commit 8c7936b

11 files changed

+8503
-0
lines changed

.babelrc

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"targets": {
7+
"node": "current"
8+
}
9+
}
10+
]
11+
]
12+
}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
dist/

LICENCE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 The GraphQL Guide
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
[![npm version](https://badge.fury.io/js/apollo-datasource-mongodb.svg)](https://www.npmjs.com/package/apollo-datasource-mongodb)
2+
3+
Apollo [data source](https://www.apollographql.com/docs/apollo-server/features/data-sources) for MongoDB
4+
5+
```
6+
npm i apollo-datasource-mongodb
7+
```
8+
9+
This package uses [DataLoader](https://github.com/graphql/dataloader) for batching and per-request memoization caching. It also optionally (if you provide a `ttl`), does shared application-level caching (using either the default Apollo `InMemoryLRUCache` or the [cache you provide to ApolloServer()](https://www.apollographql.com/docs/apollo-server/features/data-sources#using-memcachedredis-as-a-cache-storage-backend)). It does this only for these two methods, which are added to your collections:
10+
11+
- [`findOneById(id, options)`](#findonebyid)
12+
- [`findManyByIds(ids, options)`](#findmanybyids)
13+
14+
15+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
16+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
17+
**Contents:**
18+
19+
- [Usage](#usage)
20+
- [Batching](#batching)
21+
- [Caching](#caching)
22+
- [API](#api)
23+
- [findOneById](#findonebyid)
24+
- [findManyByIds](#findmanybyids)
25+
- [deleteFromCacheById](#deletefromcachebyid)
26+
27+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
28+
29+
30+
## Usage
31+
32+
The basic setup is subclassing `MongoDataSource`, setting your collections in the constructor, and then using the [API methods](#API) on your collections:
33+
34+
```js
35+
import { MongoDataSource } from 'apollo-datasource-mongodb'
36+
37+
class MyMongo extends MongoDataSource {
38+
constructor() {
39+
super()
40+
this.collections = [users, posts]
41+
}
42+
43+
getUser(userId) {
44+
return users.findOneById(userId)
45+
}
46+
}
47+
```
48+
49+
### Batching
50+
51+
This is the main feature, and is always enabled. Here's a full example:
52+
53+
```js
54+
import { MongoClient } from 'mongodb'
55+
import { MongoDataSource } from 'apollo-datasource-mongodb'
56+
import { ApolloServer } from 'apollo-server'
57+
58+
let users
59+
let posts
60+
61+
const client = new MongoClient('mongodb://localhost:27017')
62+
63+
client.connect(e => {
64+
users = client.db('users')
65+
posts = client.db('posts')
66+
})
67+
68+
class MyMongo extends MongoDataSource {
69+
constructor() {
70+
super()
71+
this.collections = [users, posts]
72+
}
73+
74+
getUser(userId) {
75+
return users.findOneById(userId)
76+
}
77+
78+
getPosts(postIds) {
79+
return posts.findManyByIds(postIds)
80+
}
81+
}
82+
83+
const resolvers = {
84+
Post: {
85+
author: (post, _, { db }) => db.getUser(post.authorId)
86+
},
87+
User: {
88+
posts: (user, _, { db }) => db.getPosts(user.postIds)
89+
}
90+
}
91+
92+
const server = new ApolloServer({
93+
typeDefs,
94+
resolvers,
95+
dataSources: () => ({
96+
db: new MyMongo()
97+
})
98+
})
99+
```
100+
101+
You might prefer to structure it as one data source per collection, in which case you'd do:
102+
103+
```js
104+
class Users extends MongoDataSource {
105+
constructor() {
106+
super()
107+
this.collections = [users]
108+
}
109+
110+
getUser(userId) {
111+
return users.findOneById(userId)
112+
}
113+
}
114+
115+
class Posts extends MongoDataSource {
116+
constructor() {
117+
super()
118+
this.collections = [posts]
119+
}
120+
121+
getPosts(postIds) {
122+
return posts.findManyByIds(postIds)
123+
}
124+
}
125+
126+
const resolvers = {
127+
Post: {
128+
author: (post, _, { users }) => users.getUser(post.authorId)
129+
},
130+
User: {
131+
posts: (user, _, { posts }) => posts.getPosts(user.postIds)
132+
}
133+
}
134+
135+
const server = new ApolloServer({
136+
typeDefs,
137+
resolvers,
138+
dataSources: () => ({
139+
users: new Users(),
140+
posts: new Posts()
141+
})
142+
})
143+
```
144+
145+
This is purely a code structure choice—it doesn't affect batching or caching. The latter option probably makes more sense if you have more than a few methods in your class.
146+
147+
### Caching
148+
149+
To enable shared application-level caching, you do everything from the above section, and you add the `ttl` option to `findOneById()`:
150+
151+
```js
152+
const MINUTE = 60
153+
154+
class MyMongo extends MongoDataSource {
155+
constructor() {
156+
super()
157+
this.collections = [users, posts]
158+
}
159+
160+
getUser(userId) {
161+
return users.findOneById(userId, { ttl: MINUTE })
162+
}
163+
164+
updateUserName(userId, newName) {
165+
users.deleteFromCacheById(userId)
166+
return users.updateOne({
167+
_id: userId
168+
}, {
169+
$set: { name: newName }
170+
})
171+
}
172+
}
173+
174+
const resolvers = {
175+
User: {
176+
posts: (user, _, { db }) => db.getPosts(user.postIds)
177+
},
178+
Mutation: {
179+
changeName: (_, { userId, newName }, { db, currentUserId }) =>
180+
currentUserId === userId && db.updateUserName(userId, newName)
181+
}
182+
}
183+
```
184+
185+
Here we also call [`deleteFromCacheById()`](#deletefromcachebyid) to remove the user from the cache when the user's data changes. If we're okay with people receiving out-of-date data for the duration of our `ttl`—in this case, for as long as a minute—then we don't need to bother adding calls to `deleteFromCacheById()`.
186+
187+
## API
188+
189+
### findOneById
190+
191+
`collection.findOneById(id, { ttl })`
192+
193+
Resolves to the found document. Uses DataLoader to load `id`. DataLoader uses `collection.find({ _id: { $in: ids } })`. Optionally caches the document if `ttl` is set (in whole seconds).
194+
195+
### findManyByIds
196+
197+
`collection.findManyByIds(ids, { ttl })`
198+
199+
Calls [`findOneById()`](#findonebyid) for each id. Resolves to an array of documents.
200+
201+
### deleteFromCacheById
202+
203+
`collection.deleteFromCacheById(id)`
204+
205+
Deletes a document from the cache.

0 commit comments

Comments
 (0)