Comments (10)
I don't know much about sequelize, but in GraphQL the way to do this would be to pass the parent to the child by including it in the resolved value. Something like this:
Parent: {
children(parent){
return parent.children.map( child => [parent, child] ); // or { parent, child }
},
}
from graphql-tools.
@HriBB Unfortunately there's not that much good advanced reading material out there for GraphQL yet. We're working on that.
The thing dataloader helps you do is not create queries with JOIN in them, but to batch togeter all requests at the same level. So say you want to get a list of N posts and info about all their authors. Potentially you could be doing N + 1 queries. With a JOIN you could do one (bigger and slower) query. With proper batching, you'll be doing two queries. One for the post and one for the N authors. Depending on what the latency of your database is, the 2 queries can actually be more efficient than the large JOIN query. It's hard to know for sure though and depends on the situation, which is why we're building a tool called tracer that will collect data and help people figure out how to make their server more efficient.
from graphql-tools.
And there's the DataLoader :)
Just for the reference, in case someone else reads this topic
from graphql-tools.
Thanks @helfer
from graphql-tools.
@helfer so I tried your suggestion, but it did not work as expected. Children resolver is expected to return an array of Location
objects, not an array of [parent, child]˙ arrays or
{parent, child}` objects.
I did manage to get it working with context though ...
The resolve function's optional third argument is a context value that
is provided to every resolve function within an execution. It is commonly
used to represent an authenticated user, or request-specific caches.
Here's my solution in case someone else has similar question.
const resolveFunctions = {
RootQuery: {
location(root, { slug }, context, options){
context.locations = {}
context.routes = {}
context.files = {}
return Location.find({ where: { slug }}).then(location => {
// store location references into context, mapped by id
context.locations[location.id] = location
return location
})
},
},
Location: {
locations(location, args, context){
return location.getLocations().then(locations => {
// store location children into context, mapped by id
locations.forEach(location => context.locations[location.id] = location)
return locations
})
},
files(location, args, context){
return location.getFiles().then(files => {
// store file references into context, mapped by id
files.forEach(files => context.files[files.id] = files)
return files
})
},
routes(location, args, context){
return location.getRoutes()
},
url(location, args, context){
// access data from context
return location.parent_id
? getLocationUrl(context.locations[location.parent_id], location)
: getLocationUrl(location)
}
},
File: {
url(file, args, context){
// access data from context
const loc = context.locations[file.location_id]
const location = loc.parent_id ? context.locations[loc.parent_id] : loc
const sector = loc.parent_id ? loc : null
return getLocationUrl(location, sector, file)
}
},
Route: {
url(route, args, context) {
// access data from context
const loc = context.locations[route.location_id]
const location = loc.parent_id ? context.locations[loc.parent_id] : loc
const sector = loc.parent_id ? loc : null
const file = context.files[route.file_id]
return getLocationUrl(location, sector, file, route)
}
}
}
Is this OK, or is there a better way of doing it?
BTW is this related to the Loader/Connector API I've been reading about in #21?
from graphql-tools.
@HriBB: Passing it through the context is probably not a great solution, because the context is shared by all resolvers, and you have no control over the order in which they run.
Reading this again, I'm not sure why passing the location object is not enough? Aren't you just calling a function on the location? If you pass the sequelize model of that location, it should just work.
from graphql-tools.
@helfer yeah I figured that the order of resolvers would be the problem with the context.
I can attach parent to each child like this
const resolveFunctions = {
RootQuery: {
location(root, { slug }, context, options){
return Location.find({ where: { slug }})
}
},
Location: {
locations(parent, args, context){
// attach parent to each location
return parent.getLocations()
.then(locations => locations.map(location => Object.assign(location, { parent })))
},
url(location, args, context){
// use the attached parent
return location.parent
? getLocationUrl(location.parent, location)
: getLocationUrl(location)
}
}
}
It would be cool if things like this would be explained in the docs. Are there any examples?
from graphql-tools.
@HriBB I think this is basically how resolvers work. Maybe we can add a paragraph that explains that? I wrote a medium post about a week ago that could be used as a starting point: https://medium.com/apollo-stack/graphql-explained-5844742f195e
from graphql-tools.
@helfer great reading, we need more articles like this.
Note: This example was slightly simplified. A real production GraphQL server would use batching and caching to reduce the number of requests to the backend and avoid making redundant ones — like fetching the same author twice. But that’s a topic for another post!
This is exactly what I need! Can't wait for another post :) And just to clarify the above quote ... by backend you mean database GraphQL server?
I would love to see some more complex examples on this topic. I can help with that, but first I need to understand how things work. Problem with apollo-server is that it is so f***ing simple to set up, that I skipped the reading part and went straight to the implementation and now I wanna do complex stuff without understanding the basics :P Need to do some research ... Can you recommend some good articles?
Let's say I have a simple GraphQL query like this:
{
posts {
id
title
author {
name
}
}
}
First I fetch all posts, then for each post fetch the author. So if I have 100 posts, obviously I don't want to run 100 queries for each post author, I wanna batch them together, either with a INNER JOIN users
or with WHERE post_id IN(post_ids)
. But where do we store post_ids for example? In context? Should we even do this? Are there any official recommendations?
Furthermore, what if some resolvers "depend" on other resolvers? I know that this is not possible, but in my case, I need it. I have a hierarchy like this parent > child > image > route
and the urls look like this
parent
http://domain.com/location/parent-slug
child
http://domain.com/location/parent-slug/child-slug
image
http://domain.com/location/parent-slug/child-slug/image-slug
route
http://domain.com/location/parent-slug/child-slug/image-slug/route-slug
I want to be able to generate urls on the server, and this is where things get complicated. When I try to resolve the image url for example, I need all the players: getRouteUrl(parent, child, image, route)
. I could fetch all from db, but then I would end up with hundreds of queries. Not really sure how to approach this ... Are there any good docs/examples?§ In traditional REST API this is easy, because I can fetch the right data at the right time, and easily pass it around to functions when needed ...
Anyway, thanks for your help @helfer. I will stop bugging you now, as I know you have better work to do ;)
from graphql-tools.
This issue offers some good reading
from graphql-tools.
Related Issues (20)
- GraphQLSchema from stitchSchema causes scalar values to be parsed twice HOT 1
- Allow sorting only top level declarations in schema HOT 1
- `@graphql-tools/mock` - controllable randomness when mocking out union types
- processImport seems unnecessarily slow due to graphql.print HOT 1
- SSE stream breaks when response data is large HOT 2
- Allow for empty objects in mock store HOT 2
- Apollo Subgraph entities are not resolved when using `wrapSchema` HOT 13
- getLoader in batch-delegate uses a non-unique cacheKey HOT 1
- graphql-ws executor does not resolve `connectionParams`
- Project Roadmap HOT 2
- Drop dependency on fast-url-parser HOT 2
- DEBUG=1 results in console.time() warnings
- @graphql-tools/mock does not work with Vitest HOT 5
- Deterministic (seeded) enum values
- Stitching: object field is null when all selections are delegated
- Http Executor encodes headers into query parameters which can leak Authorization headers
- Ability to get names of all the stitched queries in a middleware
- requireResolversToMatchSchema: 'warn' throws an error
- Support .astro file loading on graphql-tag-pluck HOT 1
- Troubles with connectionParams on subscriptions
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-tools.