-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from thanhhaudev/feature/dataloader-layer
init data loaders
- Loading branch information
Showing
21 changed files
with
329 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package loader | ||
|
||
import ( | ||
"context" | ||
"github.com/thanhhaudev/go-graphql/src/graph/model" | ||
"github.com/thanhhaudev/go-graphql/src/repository" | ||
) | ||
|
||
type authorLoader struct { | ||
authorRepository repository.AuthorRepository | ||
} | ||
|
||
// getAuthors implements a batch function that loads authors by IDs | ||
func (a *authorLoader) getAuthors(ctx context.Context, ids []int) ([]*model.Author, []error) { | ||
authors, err := a.authorRepository.GetByIDs(ctx, ids) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
return mapping(authors, ids), nil | ||
} | ||
|
||
func newAuthorLoader(authorRepository repository.AuthorRepository) *authorLoader { | ||
return &authorLoader{ | ||
authorRepository: authorRepository, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package loader | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/thanhhaudev/go-graphql/src/graph/model" | ||
"github.com/thanhhaudev/go-graphql/src/repository" | ||
) | ||
|
||
type bookLoader struct { | ||
bookRepository repository.BookRepository | ||
} | ||
|
||
// getBooks implements a batch function that loads books by IDs | ||
func (b *bookLoader) getBooks(ctx context.Context, ids []int) ([]*model.Book, []error) { | ||
books, err := b.bookRepository.GetByIDs(ctx, ids) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
return mapping(books, ids), nil | ||
} | ||
|
||
func newBookLoader(bookRepository repository.BookRepository) *bookLoader { | ||
return &bookLoader{ | ||
bookRepository: bookRepository, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package loader | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/thanhhaudev/go-graphql/src/graph/model" | ||
"github.com/thanhhaudev/go-graphql/src/repository" | ||
) | ||
|
||
type borrowerLoader struct { | ||
borrowerRepository repository.BorrowerRepository | ||
} | ||
|
||
// getBorrowers implements a batch function that loads borrowers by IDs | ||
func (b *borrowerLoader) getBorrowers(ctx context.Context, ids []int) ([]*model.Borrower, []error) { | ||
borrowers, err := b.borrowerRepository.GetByIDs(ctx, ids) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
return mapping(borrowers, ids), nil | ||
} | ||
|
||
func newBorrowerLoader(borrowerRepository repository.BorrowerRepository) *borrowerLoader { | ||
return &borrowerLoader{ | ||
borrowerRepository: borrowerRepository, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package loader | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/thanhhaudev/go-graphql/src/graph/model" | ||
"github.com/thanhhaudev/go-graphql/src/repository" | ||
"github.com/vikstrous/dataloadgen" | ||
) | ||
|
||
type ctxKey string | ||
|
||
type Model interface { | ||
GetID() int | ||
} | ||
|
||
const ( | ||
loadersKey = ctxKey("dataLoaders") | ||
) | ||
|
||
// Loaders wrap your data loaders to inject via middleware | ||
type Loaders struct { | ||
AuthorLoader *dataloadgen.Loader[int, *model.Author] | ||
BookLoader *dataloadgen.Loader[int, *model.Book] | ||
BorrowerLoader *dataloadgen.Loader[int, *model.Borrower] | ||
} | ||
|
||
// NewLoaders instantiates data loaders for the middleware | ||
func NewLoaders( | ||
authorRepository repository.AuthorRepository, | ||
bookRepository repository.BookRepository, | ||
borrowerRepository repository.BorrowerRepository, | ||
) *Loaders { | ||
al := newAuthorLoader(authorRepository) | ||
bl := newBookLoader(bookRepository) | ||
brl := newBorrowerLoader(borrowerRepository) | ||
w := dataloadgen.WithWait(10 * time.Millisecond) | ||
|
||
return &Loaders{ | ||
AuthorLoader: dataloadgen.NewLoader(al.getAuthors, w), | ||
BookLoader: dataloadgen.NewLoader(bl.getBooks, w), | ||
BorrowerLoader: dataloadgen.NewLoader(brl.getBorrowers, w), | ||
} | ||
} | ||
|
||
// Middleware injects data loaders into the context | ||
// This middleware likely injects data loaders into the request context, optimizing data fetching. | ||
// This accessibly allows the resolvers to load data in a batch, reducing the number of queries to the database. | ||
func Middleware( | ||
authorRepository repository.AuthorRepository, | ||
bookRepository repository.BookRepository, | ||
borrowerRepository repository.BorrowerRepository, | ||
next http.Handler, | ||
) http.Handler { | ||
// return a middleware that injects the loader to the request context | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
loader := NewLoaders(authorRepository, bookRepository, borrowerRepository) | ||
r = r.WithContext(context.WithValue(r.Context(), loadersKey, loader)) | ||
|
||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
// mapping maps the models to the same order as the input IDs | ||
func mapping[M Model](models []M, ids []int) []M { | ||
// Create a map to easily find authors by ID | ||
idMap := make(map[int]M) | ||
for _, m := range models { | ||
idMap[m.GetID()] = m | ||
} | ||
|
||
// Prepare the result slice in the same order as the input IDs | ||
// This is important because the result will be used in the same order as the input IDs | ||
result := make([]M, len(ids)) | ||
for i, id := range ids { | ||
result[i] = idMap[id] | ||
} | ||
|
||
return result | ||
} | ||
|
||
// For returns the data loader for a given context | ||
func For(ctx context.Context) *Loaders { | ||
return ctx.Value(loadersKey).(*Loaders) | ||
} | ||
|
||
// FindAuthor loads an author by ID | ||
func FindAuthor(ctx context.Context, id int) (*model.Author, error) { | ||
return For(ctx).AuthorLoader.Load(ctx, id) | ||
} | ||
|
||
// GetAuthors loads authors by IDs | ||
func GetAuthors(ctx context.Context, ids []int) ([]*model.Author, error) { | ||
return For(ctx).AuthorLoader.LoadAll(ctx, ids) | ||
} | ||
|
||
func FindBook(ctx context.Context, id int) (*model.Book, error) { | ||
return For(ctx).BookLoader.Load(ctx, id) | ||
} | ||
|
||
func GetBooks(ctx context.Context, ids []int) ([]*model.Book, error) { | ||
return For(ctx).BookLoader.LoadAll(ctx, ids) | ||
} | ||
|
||
func FindBorrower(ctx context.Context, id int) (*model.Borrower, error) { | ||
return For(ctx).BorrowerLoader.Load(ctx, id) | ||
} | ||
|
||
func GetBorrowers(ctx context.Context, ids []int) ([]*model.Borrower, error) { | ||
return For(ctx).BorrowerLoader.LoadAll(ctx, ids) | ||
} |
Oops, something went wrong.