This example shows how to avoid redundant roundtrips to data sources via batching and caching, implemented with DataLoader. This comes in handy when there are multiple nested routes that depend on the same data source.
Note that in many cases, you can meet the same requirement with Remix's own useMatches which lets you use all of your parent-routes' data in child routes. If that suits your use case, using useMatches
is preferable. There are cases where this might not be the best fit though, including:
- You can't be sure if a parent did already request a particular piece of data
- You don't want your data loading to be tightly coupled with your parent routes, for architectural reasons
Open this example on CodeSandbox:
app/data.server.ts
implements the db
object which mimics an ORM in the style of Prisma. The method db.user#findMany
logs user#findMany to the console, for demo purposes.
There's exactly one DataLoader factory createUsersByIdLoader
, implemented in app/loaders/userLoader.ts
. It's put on context of createRequestHandler in server/index.ts
as usersById
which is made available to all Remix-loaders and -actions. Both the loaders of app/routes/users.tsx
and app/routes/users/_index.tsx
make calls to this loader. When inspecting the server logs while refreshing the page, you'll notice that there's only one log user#findMany per request, proving that with this implementation, there's only one roundtrip to the database.
- DataLoader docs
- DataLoader – Source code walkthrough [video], for those interested
- Remix docs: useMatches