Skip to content

Commit 688d3f6

Browse files
committed
Only support a single collection per class
1 parent 4fb16e5 commit 688d3f6

File tree

3 files changed

+47
-98
lines changed

3 files changed

+47
-98
lines changed

README.md

Lines changed: 32 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -32,36 +32,44 @@ This package uses [DataLoader](https://github.com/graphql/dataloader) for batchi
3232

3333
### Basic
3434

35-
The basic setup is subclassing `MongoDataSource`, passing your collection(s) to the constructor, and using the [API methods](#API):
35+
The basic setup is subclassing `MongoDataSource`, passing your collection to the constructor, and using the [API methods](#API):
3636

3737
```js
3838
import { MongoDataSource } from 'apollo-datasource-mongodb'
3939

40-
class MyMongo extends MongoDataSource {
40+
export default class Users extends MongoDataSource {
4141
getUser(userId) {
42-
return this.users.findOneById(userId)
42+
return this.findOneById(userId)
4343
}
4444
}
45+
```
46+
47+
and:
48+
49+
```js
50+
import Users from './data-sources/Users.js'
51+
52+
const users = db.collection('users')
4553

4654
const server = new ApolloServer({
4755
typeDefs,
4856
resolvers,
4957
dataSources: () => ({
50-
db: new MyMongo({ users, posts })
58+
db: new Users({ users })
5159
})
5260
})
5361
```
5462

55-
The collections are available at `this.collections` (e.g. `this.collections.users.update({_id: 'foo, { $set: { name: 'me' }}})`). The request's context is available at `this.context`. For example, if you put the logged-in user's ID on context as `context.currentUserId`:
63+
The collection is available at `this.users` (e.g. `this.users.update({_id: 'foo, { $set: { name: 'me' }}})`). The request's context is available at `this.context`. For example, if you put the logged-in user's ID on context as `context.currentUserId`:
5664

5765
```js
58-
class MyMongo extends MongoDataSource {
66+
class Users extends MongoDataSource {
5967
...
6068

6169
async getPrivateUserData(userId) {
6270
const isAuthorized = this.context.currentUserId === userId
6371
if (isAuthorized) {
64-
const user = await this.users.findOneById(userId)
72+
const user = await this.findOneById(userId)
6573
return user && user.privateData
6674
}
6775
}
@@ -71,7 +79,7 @@ class MyMongo extends MongoDataSource {
7179
If you want to implement an initialize method, it must call the parent method:
7280

7381
```js
74-
class MyMongo extends MongoDataSource {
82+
class Users extends MongoDataSource {
7583
initialize(config) {
7684
super.initialize(config)
7785
...
@@ -83,69 +91,14 @@ class MyMongo extends MongoDataSource {
8391

8492
This is the main feature, and is always enabled. Here's a full example:
8593

86-
```js
87-
import { MongoClient } from 'mongodb'
88-
import { MongoDataSource } from 'apollo-datasource-mongodb'
89-
import { ApolloServer } from 'apollo-server'
90-
91-
let users
92-
let posts
93-
94-
const client = new MongoClient('mongodb://localhost:27017')
95-
96-
client.connect(e => {
97-
users = client.db('users')
98-
posts = client.db('posts')
99-
})
100-
101-
class MyMongo extends MongoDataSource {
102-
getUser(userId) {
103-
return this.users.findOneById(userId)
104-
}
105-
106-
getPosts(postIds) {
107-
return this.posts.findManyByIds(postIds)
108-
}
109-
}
110-
111-
const resolvers = {
112-
Post: {
113-
author: (post, _, { db }) => db.getUser(post.authorId)
114-
},
115-
User: {
116-
posts: (user, _, { db }) => db.getPosts(user.postIds)
117-
}
118-
}
119-
120-
const server = new ApolloServer({
121-
typeDefs,
122-
resolvers,
123-
dataSources: () => ({
124-
db: new MyMongo({ users, posts })
125-
})
126-
})
127-
```
128-
129-
You might prefer to structure it as one data source per collection, in which case you'd set `this.collection` instead of `this.collections`, and the [API methods](#api) would be available directly on `this`:
130-
13194
```js
13295
class Users extends MongoDataSource {
133-
constructor() {
134-
super()
135-
this.collection = users
136-
}
137-
13896
getUser(userId) {
13997
return this.findOneById(userId)
14098
}
14199
}
142100

143101
class Posts extends MongoDataSource {
144-
constructor() {
145-
super()
146-
this.collection = posts
147-
}
148-
149102
getPosts(postIds) {
150103
return this.findManyByIds(postIds)
151104
}
@@ -160,38 +113,34 @@ const resolvers = {
160113
}
161114
}
162115

116+
const users = db.collection('users')
117+
const posts = db.collection('posts')
118+
163119
const server = new ApolloServer({
164120
typeDefs,
165121
resolvers,
166122
dataSources: () => ({
167-
users: new Users(),
168-
posts: new Posts()
123+
users: new Users({ users }),
124+
posts: new Posts({ posts })
169125
})
170126
})
171127
```
172128

173-
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.
174-
175129
### Caching
176130

177131
To enable shared application-level caching, you do everything from the above section, and you add the `ttl` option to `findOneById()`:
178132

179133
```js
180134
const MINUTE = 60
181135

182-
class MyMongo extends MongoDataSource {
183-
constructor() {
184-
super()
185-
this.collections = { users, posts }
186-
}
187-
136+
class Users extends MongoDataSource {
188137
getUser(userId) {
189-
return this.users.findOneById(userId, { ttl: MINUTE })
138+
return this.findOneById(userId, { ttl: MINUTE })
190139
}
191140

192141
updateUserName(userId, newName) {
193-
this.users.deleteFromCacheById(userId)
194-
return users.updateOne({
142+
this.deleteFromCacheById(userId)
143+
return this.users.updateOne({
195144
_id: userId
196145
}, {
197146
$set: { name: newName }
@@ -200,12 +149,12 @@ class MyMongo extends MongoDataSource {
200149
}
201150

202151
const resolvers = {
203-
User: {
204-
posts: (user, _, { db }) => db.getPosts(user.postIds)
152+
Post: {
153+
author: (post, _, { users }) => users.getUser(post.authorId)
205154
},
206155
Mutation: {
207-
changeName: (_, { userId, newName }, { db, currentUserId }) =>
208-
currentUserId === userId && db.updateUserName(userId, newName)
156+
changeName: (_, { userId, newName }, { users, currentUserId }) =>
157+
currentUserId === userId && users.updateUserName(userId, newName)
209158
}
210159
}
211160
```
@@ -216,18 +165,18 @@ Here we also call [`deleteFromCacheById()`](#deletefromcachebyid) to remove the
216165

217166
### findOneById
218167

219-
`findOneById(id, { ttl })`
168+
`this.findOneById(id, { ttl })`
220169

221170
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).
222171

223172
### findManyByIds
224173

225-
`findManyByIds(ids, { ttl })`
174+
`this.findManyByIds(ids, { ttl })`
226175

227176
Calls [`findOneById()`](#findonebyid) for each id. Resolves to an array of documents.
228177

229178
### deleteFromCacheById
230179

231-
`deleteFromCacheById(id)`
180+
`this.deleteFromCacheById(id)`
232181

233182
Deletes a document from the cache.

src/__tests__/datasource.test.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { MongoDataSource } from '../datasource'
22

33
const users = {}
4-
const posts = {}
54

6-
class MyMongo extends MongoDataSource {
5+
class Users extends MongoDataSource {
76
initialize(config) {
87
super.initialize(config)
98
}
109
}
1110

1211
describe('MongoDataSource', () => {
1312
it('sets up caching functions', () => {
14-
const source = new MyMongo({ users, posts })
13+
const source = new Users({ users })
1514
source.initialize({})
16-
expect(source.users.findOneById).toBeDefined()
17-
expect(source.posts.findOneById).toBeDefined()
15+
expect(source.findOneById).toBeDefined()
16+
expect(source.users).toEqual(users)
1817
})
1918
})

src/datasource.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import { InMemoryLRUCache } from 'apollo-server-caching'
55
import { createCachingMethods } from './cache'
66

77
class MongoDataSource extends DataSource {
8-
constructor(collections) {
8+
constructor(collection) {
99
super()
1010

11-
const setUpCorrectly = typeof collections === 'object'
11+
const setUpCorrectly =
12+
typeof collection === 'object' && Object.keys(collection).length === 1
1213
if (!setUpCorrectly) {
1314
throw new ApolloError(
14-
'MongoDataSource constructor must be given an object with collection(s)'
15+
'MongoDataSource constructor must be given an object with a single collection'
1516
)
1617
}
1718

18-
this.collections = collections
19+
this.collectionName = Object.keys(collection)[0]
20+
this[this.collectionName] = collection[this.collectionName]
1921
}
2022

2123
// https://github.com/apollographql/apollo-server/blob/master/packages/apollo-datasource/src/index.ts
@@ -24,12 +26,11 @@ class MongoDataSource extends DataSource {
2426

2527
const cache = config.cache || new InMemoryLRUCache()
2628

27-
for (const key in this.collections) {
28-
this[key] = createCachingMethods({
29-
collection: this.collections[key],
30-
cache
31-
})
32-
}
29+
const methods = createCachingMethods({
30+
collection: this[this.collectionName],
31+
cache
32+
})
33+
Object.assign(this, methods)
3334
}
3435
}
3536

0 commit comments

Comments
 (0)