GithubHelp home page GithubHelp logo

Comments (10)

helfer avatar helfer commented on April 30, 2024 7

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.

helfer avatar helfer commented on April 30, 2024 3

@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.

HriBB avatar HriBB commented on April 30, 2024 1

And there's the DataLoader :)
Just for the reference, in case someone else reads this topic

from graphql-tools.

HriBB avatar HriBB commented on April 30, 2024

Thanks @helfer

from graphql-tools.

HriBB avatar HriBB commented on April 30, 2024

@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.

helfer avatar helfer commented on April 30, 2024

@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.

HriBB avatar HriBB commented on April 30, 2024

@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.

helfer avatar helfer commented on April 30, 2024

@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.

HriBB avatar HriBB commented on April 30, 2024

@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.

HriBB avatar HriBB commented on April 30, 2024

This issue offers some good reading

from graphql-tools.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.