Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/totbwf/fsharp.data.dataloader
A F# Library for simplifying access to remote data, with caching and batching
https://github.com/totbwf/fsharp.data.dataloader
batching caching dataloader fsharp
Last synced: 3 months ago
JSON representation
A F# Library for simplifying access to remote data, with caching and batching
- Host: GitHub
- URL: https://github.com/totbwf/fsharp.data.dataloader
- Owner: TOTBWF
- License: mit
- Created: 2017-06-23T21:35:22.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2017-10-18T22:42:51.000Z (over 7 years ago)
- Last Synced: 2023-02-26T23:26:31.424Z (almost 2 years ago)
- Topics: batching, caching, dataloader, fsharp
- Language: F#
- Size: 25.4 KB
- Stars: 11
- Watchers: 3
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# FSharp.Data.Dataloader
### What is Dataloader?
Dataloader is a generic utility meant to be used in your application's data fetch layer to
provide batching and caching.This is a spiritual port of the [Haxl](https://github.com/facebook/Haxl) library for Haskell.
## Getting Started
First, install and build the library.```sh
cd src
dotnet restore
dotnet build
```
For our example, we are going to show how to make a simple blog using Dataloader.First we are going to make an instance of a request:
```fsharp
type BlogRequest<'a> =
| FetchPosts of (int list -> 'a)
| FetchPostContent of int * (string option -> 'a)
interface Request<'a> with
member x.Identifier =
match x with
| FetchPosts -> "FetchPosts"
| FetchPostContent id -> "FetchContent id = " + id.ToString()
```A request encapsulates all of the possible requests you could make to a given datasource, as well as the data needed to make those requests.
Next up we are going to create our DataSource
```fsharp
let blogDataSource() =
let fetchfn (blocked: BlockedFetch<'a, BlogRequest<'a>> list) =
blocked
|> List.iter(fun b ->
match b.Request with
| FetchPosts cont ->
let ids = env.PostIds
FetchResult.putSuccess (b.Status) (cont ids)
| FetchPostContent(id, cont) ->
let content = Map.tryFind id env.PostContents
FetchResult.putSuccess (b.Status) (cont content))
|> SyncFetch
DataSource.create "Blog" fetchfn
```The most important part of our datasource is the `fetchfn` function, which describes how we are going to resolve a batch of requests that we are currently blocking on. Note that it returns a `PerformFetch`, which is defined as such:
```fsharp
type PerformFetch =
| SyncFetch of unit
| AsyncFetch of Async
```
This allows us to do fully asynchronous requests, but for now we are just going to use the synchronous version.Finally, let us see it all composed together:
```fsharp
let fetchPostIds = Fetch.dataFetch> (blogDataSource()) (FetchPosts id)
let fetchPostContent postId = Fetch.dataFetch> source (FetchPostContent(postId, id))
let renderPosts posts postIds =
List.zip posts postIds
|> List.map(fun (c, id) -> sprintf "Id: %d\n Post: %s" c id) |> List.fold(fun acc e -> e + "\n" + acc) ""
let posts =
fetchPostIds
|> Fetch.bind(Fetch.mapSeq fetchPostContent)
|> Fetch.map(Seq.map(Option.defaultWith(fun () -> "")) >> Seq.toList)
let contents =
fetchPostIds
|> Fetch.map(renderPosts)
|> Fetch.applyTo posts
let res = Fetch.runFetch contents
```Cool! Even though we call `fetchPostIds` twice, it will only make that fetch once! This example doesnt gain that much from request batching, but if we decided to implement it (say, if we had a SQL datasource), we would implement it within the `FetchFn` of our DataSource.