Skip to content

Commit dda2902

Browse files
Adds Graphiti example to docs
1 parent 010aa72 commit dda2902

File tree

1 file changed

+110
-13
lines changed

1 file changed

+110
-13
lines changed

README.md

Lines changed: 110 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,34 @@ This is a Swift version of the Facebook [DataLoader](https://github.com/facebook
66
[![Swift][swift-badge]][swift-url]
77
[![License][mit-badge]][mit-url]
88

9-
## Installation 💻
9+
## Gettings started 🚀
1010

1111
Include this repo in your `Package.swift` file.
1212

1313
```swift
1414
.Package(url: "https://github.com/GraphQLSwift/DataLoader.git", .upToNextMajor(from: "1.1.0"))
1515
```
1616

17-
## Gettings started 🚀
18-
### Batching
17+
To get started, create a DataLoader. Each DataLoader instance represents a unique cache. Typically instances are created per request when used
18+
within a web-server if different users can see different things.
19+
20+
## Batching
1921
Batching is not an advanced feature, it's DataLoader's primary feature.
2022

2123
Create a DataLoader by providing a batch loading function:
2224
```swift
2325
let userLoader = Dataloader<Int, User>(batchLoadFunction: { keys in
2426
try User.query(on: req).filter(\User.id ~~ keys).all().map { users in
25-
return users.map { DataLoaderFutureValue.success($0) }
27+
keys.map { key in
28+
DataLoaderFutureValue.success(users.filter{ $0.id == key })
29+
}
2630
}
2731
})
2832
```
29-
#### Load individual keys
33+
34+
The order of the returned DataLoaderFutureValues must match the order of the keys.
35+
36+
### Load individual keys
3037
```swift
3138
let future1 = try userLoader.load(key: 1, on: eventLoopGroup)
3239
let future2 = try userLoader.load(key: 2, on: eventLoopGroup)
@@ -35,13 +42,13 @@ let future3 = try userLoader.load(key: 1, on: eventLoopGroup)
3542

3643
The example above will only fetch two users, because the user with key `1` is present twice in the list.
3744

38-
#### Load multiple keys
45+
### Load multiple keys
3946
There is also a method to load multiple keys at once
4047
```swift
4148
try userLoader.loadMany(keys: [1, 2, 3], on: eventLoopGroup)
4249
```
4350

44-
#### Execution
51+
### Execution
4552
By default, a DataLoader will wait for a short time (2ms) from the moment `load` is called to collect keys prior
4653
to running the `batchLoadFunction` and completing the `load` futures. This is to let keys accumulate and
4754
batch into a smaller number of total requests. This amount of time is configurable using the `executionPeriod`
@@ -65,12 +72,12 @@ If desired, you can manually execute the `batchLoadFunction` and complete the fu
6572
Scheduled execution can be disabled by setting `executionPeriod` to `nil`, but be careful - you *must* call `.execute()`
6673
manually in this case. Otherwise, the futures will never complete.
6774

68-
#### Disable batching
75+
### Disable batching
6976
It is possible to disable batching by setting the `batchingEnabled` option to `false`
7077
It will invoke the `batchLoadFunction` immediately when a key is loaded.
7178

7279

73-
### Caching
80+
## Caching
7481

7582
DataLoader provides a memoization cache. After `.load()` is called with a key, the resulting value is cached
7683
for the lifetime of the DataLoader object. This eliminates redundant loads.
@@ -85,7 +92,7 @@ let future2 = userLoader.load(key: 1, on: eventLoopGroup)
8592
assert(future1 === future2)
8693
```
8794

88-
#### Caching per-Request
95+
### Caching per-Request
8996

9097
DataLoader caching *does not* replace Redis, Memcache, or any other shared
9198
application-level cache. DataLoader is first and foremost a data loading mechanism,
@@ -98,7 +105,7 @@ could result in cached data incorrectly appearing in each request. Typically,
98105
DataLoader instances are created when a Request begins, and are not used once the
99106
Request ends.
100107

101-
#### Clearing Cache
108+
### Clearing Cache
102109

103110
In certain uncommon cases, clearing the request cache may be necessary.
104111

@@ -124,7 +131,7 @@ userLoader.load(key: 4, on: eventLoopGroup)
124131
// Request completes.
125132
```
126133

127-
#### Caching Errors
134+
### Caching Errors
128135

129136
If a batch load fails (that is, a batch function throws or returns a DataLoaderFutureValue.failure(Error)),
130137
then the requested values will not be cached. However if a batch
@@ -142,7 +149,7 @@ userLoader.load(key: 1, on: eventLoopGroup).catch { error in {
142149
}
143150
```
144151

145-
#### Disabling Cache
152+
### Disabling Cache
146153

147154
In certain uncommon cases, a DataLoader which *does not* cache may be desirable.
148155
Calling `DataLoader(options: DataLoaderOptions(cachingEnabled: false), batchLoadFunction: batchLoadFunction)` will ensure that every
@@ -184,6 +191,94 @@ let myLoader = DataLoader<String, String>(batchLoadFunction: { keys in
184191
})
185192
```
186193

194+
## Using with GraphQL
195+
196+
DataLoader pairs nicely well with [GraphQL](https://github.com/GraphQLSwift/GraphQL) and
197+
[Graphiti](https://github.com/GraphQLSwift/Graphiti). GraphQL fields are designed to be
198+
stand-alone functions. Without a caching or batching mechanism,
199+
it's easy for a naive GraphQL server to issue new database requests each time a
200+
field is resolved.
201+
202+
Consider the following GraphQL request:
203+
204+
```
205+
{
206+
me {
207+
name
208+
bestFriend {
209+
name
210+
}
211+
friends(first: 5) {
212+
name
213+
bestFriend {
214+
name
215+
}
216+
}
217+
}
218+
}
219+
```
220+
221+
Naively, if `me`, `bestFriend` and `friends` each need to request the backend,
222+
there could be at most 13 database requests!
223+
224+
By using DataLoader, we could batch our requests to a `User` type, and
225+
only require at most 4 database requests, and possibly fewer if there are cache hits.
226+
Here's a full example using Graphiti:
227+
228+
```swift
229+
struct User : Codable {
230+
let id: Int
231+
let name: String
232+
let bestFriendID: Int
233+
let friendIDs: [Int]
234+
235+
func getBestFriend(context: UserContext, arguments: NoArguments, group: EventLoopGroup) throws -> EventLoopFuture<User> {
236+
return try context.userLoader.load(key: user.bestFriendID, on: group)
237+
}
238+
239+
struct FriendArguments {
240+
first: Int
241+
}
242+
func getFriends(context: UserContext, arguments: FriendArguments, group: EventLoopGroup) throws -> EventLoopFuture<[User]> {
243+
return try context.userLoader.loadMany(keys: user.friendIDs[0..<arguments.first], on: group)
244+
}
245+
}
246+
247+
struct UserResolver {
248+
public func me(context: UserContext, arguments: NoArguments) -> User {
249+
...
250+
}
251+
}
252+
253+
class UserContext {
254+
let database = ...
255+
let userLoader = DataLoader<Int, User>() { keys in
256+
return User.query(on: database).filter(\.$id ~~ keys).all().map { users in
257+
keys.map { key in
258+
users.first { $0.id == key }!
259+
}
260+
}
261+
}
262+
}
263+
264+
struct UserAPI : API {
265+
let resolver = UserResolver()
266+
let schema = Schema<UserResolver, UserContext> {
267+
Type(User.self) {
268+
Field("name", at: \.content)
269+
Field("bestFriend", at: \.getBestFriend, as: TypeReference<User>.self)
270+
Field("friends", at: \.getFriends, as: [TypeReference<User>]?.self) {
271+
Argument("first", at: .\first)
272+
}
273+
}
274+
275+
Query {
276+
Field("me", at: UserResolver.hero, as: User.self)
277+
}
278+
}
279+
}
280+
```
281+
187282
## Contributing 🤘
188283

189284
All your feedback and help to improve this project is very welcome. Please create issues for your bugs, ideas and
@@ -201,6 +296,8 @@ This library is entirely a Swift version of Facebooks [DataLoader](https://githu
201296
Developed by [Lee Byron](https://github.com/leebyron) and [Nicholas Schrock](https://github.com/schrockn)
202297
from [Facebook](https://www.facebook.com/).
203298

299+
300+
204301
[swift-badge]: https://img.shields.io/badge/Swift-5.2-orange.svg?style=flat
205302
[swift-url]: https://swift.org
206303

0 commit comments

Comments
 (0)