@@ -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
1111Include 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
1921Batching is not an advanced feature, it's DataLoader's primary feature.
2022
2123Create a DataLoader by providing a batch loading function:
2224``` swift
2325let 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
3138let future1 = try userLoader.load (key : 1 , on : eventLoopGroup)
3239let future2 = try userLoader.load (key : 2 , on : eventLoopGroup)
@@ -35,13 +42,13 @@ let future3 = try userLoader.load(key: 1, on: eventLoopGroup)
3542
3643The 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
3946There is also a method to load multiple keys at once
4047``` swift
4148try userLoader.loadMany (keys : [1 , 2 , 3 ], on : eventLoopGroup)
4249```
4350
44- #### Execution
51+ ### Execution
4552By default, a DataLoader will wait for a short time (2ms) from the moment ` load ` is called to collect keys prior
4653to running the ` batchLoadFunction ` and completing the ` load ` futures. This is to let keys accumulate and
4754batch 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
6572Scheduled execution can be disabled by setting ` executionPeriod ` to ` nil ` , but be careful - you * must* call ` .execute() `
6673manually in this case. Otherwise, the futures will never complete.
6774
68- #### Disable batching
75+ ### Disable batching
6976It is possible to disable batching by setting the ` batchingEnabled ` option to ` false `
7077It will invoke the ` batchLoadFunction ` immediately when a key is loaded.
7178
7279
73- ### Caching
80+ ## Caching
7481
7582DataLoader provides a memoization cache. After ` .load() ` is called with a key, the resulting value is cached
7683for the lifetime of the DataLoader object. This eliminates redundant loads.
@@ -85,7 +92,7 @@ let future2 = userLoader.load(key: 1, on: eventLoopGroup)
8592assert (future1 === future2)
8693```
8794
88- #### Caching per-Request
95+ ### Caching per-Request
8996
9097DataLoader caching * does not* replace Redis, Memcache, or any other shared
9198application-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,
98105DataLoader instances are created when a Request begins, and are not used once the
99106Request ends.
100107
101- #### Clearing Cache
108+ ### Clearing Cache
102109
103110In 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
129136If a batch load fails (that is, a batch function throws or returns a DataLoaderFutureValue.failure(Error)),
130137then 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
147154In certain uncommon cases, a DataLoader which * does not* cache may be desirable.
148155Calling `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
189284All 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
201296Developed by [Lee Byron](https :// github.com/leebyron) and [Nicholas Schrock](https://github.com/schrockn)
202297from [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