facebook / relay

Relay is a JavaScript framework for building data-driven React applications.
https://relay.dev
MIT License
18.41k stars 1.83k forks source link

Support Root Fields w/o Node Mapping #112

Closed yungsters closed 8 years ago

yungsters commented 9 years ago

Problem

Relay currently only knows how to handle these three types of root fields:

However, it has become clear that Relay needs to support any kind of root field. For example:

For now, the unsupported use cases can be implemented by creating a "global node", commonly called the viewer. You can then add arbitrary fields to viewer.

static fragments = {
  viewer: () => Relay.QL`
    fragment on Viewer {
      # Can return an array of users.
      users
    }
  `
};

Rationale

Historically, GraphQL (as used internally at Facebook) did not have a root type or root fields. Instead, it had special "root calls" such as node, nodes, me, and usernames. Much of Relay was built on top of this assumption that the "root calls" return nodes.

For example, when we fetch me and get {id: "123", ...}, we record the association between the me root field and the node with ID of 123. Now, if we ever encounter another query for me, we can check our store for the node with ID of 123 and resolve the query without having to potentially re-fetch all of the fields we already have for me.

Another example, when we fetch nodes(ids: ["123", "456"]), we record the association between each argument and their respective response nodes. This allows us to fulfill queries for node(id: "123") and node(id: "456") even though we may never have independently queried for either before. (We would also be able to fulfill me if the association from above was established.)

Next Steps

devknoll commented 9 years ago

@yungsters @josephsavona is it possible that this change would also allow us to remove the Relay-specific Input Objects on mutations? It seems super strange that GraphQL supports multiple arguments but then Relay forces them to all be wrapped up ;-)

Edit: would lose the ability to have a single variable in the document and stuff all the values into a variable as an input object though.

vincentriemer commented 9 years ago

Do you have any example code as to how the viewer root field would look like on the server side (nodejs)?

josephsavona commented 9 years ago

@vincentriemer take a look at the todo app's schema for an example of setting up a root viewer field.

nickretallack commented 9 years ago

This really confused me. This limitation should be spelled out more clearly in the docs and in relay-starter-kit.

There is already a root object: the GraphQLSchema. Its direct descendants can be GraphQLObjectTypes with fields in them that can be other GraphQLObjectTypes. However, it seems that the first level GraphQLObjectTypes are different from all others in that Relay refuses to query them in certain ways.

In the starter kit, the root object is queryType, and it defines a field viewer which is a userType. queryType even has a comment in it: // Add your own root fields here. But you don't want to add your own root fields here because they will have limited functionality. You really want to add them into the viewer. So does it make sense for the viewer to be a userType? I don't think it does.

I think the starter kit should change the userType into a generic wrapper type and move the comment into that type instead.

Btw, I really wanted to create a connection type at the root level. Requiring two levels of GraphQLObjectType in a row to get to normal functionality seems silly.

As for making things continue to work at Facebook, I don't know the specifics, but it seems like removing this restriction shouldn't negatively impact anything, right? You'd just need to make Relay's parser smart enough to parse queries that don't fall under this restriction. If you wanted to keep the restriction internally, you could write some sort of validation process to check if anyone is violating the restriction before submitting their code.

steveluscher commented 9 years ago

Thanks for this, @nickretallack. Right now, I'm working on literally nothing else but enabling identifying/non-identifying singular/plural fields at the root, including connections. Hold tight!

RavenHursT commented 9 years ago

:+1:

stevewillard commented 8 years ago

Just ran into this. Would love to see root fields with multiple arguments!

ForbesLindesay commented 8 years ago

One problem with the viewer approach is that Relay seems not to let you specify multiple levels of nesting at the root query level:

const AppContainer = Relay.createContainer(App, {
  fragments: {
    projects: () => Relay.QL`
      fragment on Project {
        name
        isMyProject
      }
    `
  }
});

const AppQueries = {
  projects: () => Relay.QL`
    query {
      viewer {
        projects
      }
    }
  `
};

ReactDOM.render(
  <RelayRouter history={history}>
    <Route path="/" component={AppContainer} queries={AppQueries} />
  </RelayRouter>,
  mountNode
);

Is there a workaround for making that work? Or should my root query always just be statically set to query { viewer }?

josephsavona commented 8 years ago

@ForbesLindesay There's no technical reason we couldn't allow nesting in the root query. The only thing that would have to change is GraphQLFragmentPointer's createForRoot method, which makes a simplifying assumption that the fragment is a direct child - any interest in sending a PR? ;-)

Otherwise, yes, most apps end up with mostly viewer/node root queries.

RavenHursT commented 8 years ago

@steveluscher has there been any progress on this? Is there anyway I can help? Right now, we're basically using JSON.stringify() to send in multiple arguments via Relay to a root query. It's a very hacky solution, but seems to be working. Would love to be able to remove this cruft from our project sooner rather than later ;-)

ForbesLindesay commented 8 years ago

The right solution seems like it would be to fix this issue. If you have pointers on what to do though I can try and produce a pull request. I'm not really clear on the details of what code does what.

steveluscher commented 8 years ago

@RavenHursT: Indeed, though at one time I was moving full steam on this project, it's been moved to the backburner. I laid some of the groundwork (we moved from storing an association between ‘fieldNames’ and ‘rootCallIds’ to one between a field's ‘storage key’ (a hash of its name and it sorted arguments) and its ‘identifying argument value.’ Next steps include:

  1. Extend the rootCallMap such that you can express associations between an identifying argument value (or EMPTY if there's no identifier) and one or more data IDs ({[identifyingArgValue: string]: DataID | Array<DataID>})
  2. Find every instance of forEachRootCallArg, rename it to forEachIdentifyingArgValue, and teach it to store arrays of DataID when the field in question is plural.
  3. Right now forEachRootCallArg assumes that root calls will have one argument and one argument only, and that any argument that appears is the identifying one. Tear down this assumption by creating a first-class GraphQL type GraphQLIdentifier that you can use to ‘tag’ an argument as being the ‘identifying’ one in an identifying root field. Configure the printer to print metadata that calls out the identifying argument. Teach RelayRootQuery#getIdentifyingArg to use this metadata to return the identifying argument (right now it just returns the first argument, if any).

The first two tasks should enable plural non-identifying root fields. The third one should unlock non-identifying arguments on root fields.

ryancole commented 8 years ago

@yungsters If I were to embrace the origins of Relay, and build a system of my own that has a similar "root call" named viewer, is it fair to say that viewer is sort of like the current active user's window into the nested data fields?

{
  viewer {
    accounts {
      edges { node { id, username } }
    }
  }
}

If I were currently signed in as a some user, the accounts field would take my session and authorization into account, on the back-end, and only provide me with the items I should be able to access, or any other properties that may change based on who I am?

How would this system work without the requirement of a user being signed in, but having the ability to create an account and sign in? With the accounts connection being under the viewer, it's not possible to mutate accounts and so there's a chicken/egg problem there.

I'm trying to figure out how to embrace this design, and also build a user auth system entirely on top of GraphQL / Relay.

yungsters commented 8 years ago

@ryancole You have the description of viewer correct. For example, at Facebook, viewer contains a news_feed connection field that is always the current viewer's news feed. Another notable field that viewer has is the actor field which allows querying the current user's information.

As far as I know, our internal GraphQL endpoint was not originally designed for usage without authentication. However, the data model backing viewer does support the notion of an "unauthenticated user". More recently, we've augmented the GraphQL endpoint to support fetching fields for unauthenticated users, but off the top of my head, I do not know what is and is not accessible.

What I imagine to be common with most applications that deal with authentication (and which is certainly true for Facebook) is for the entire client store to be reset when authenticating as a user. This is useful for a variety of reasons including isolation of concerns (e.g. modules that can assume there is a logged in user vs. those that do not make this assumption) as well as privacy and security (i.e. the simplest way to ensure that there is no cross-contamination or leaking of a user's private information is to start with a clean slate).

For example, most of the Facebook mobile apps built using React Native and Relay implement authentication without Relay, but — upon obtaining an access token — switch to relying on Relay for all user client data management.

This would certainly complicate things in a system where a user can authenticate using multiple accounts (e.g. something like an operating system such as Windows or OS X where a user can be logged in as multiple user simultaneously). But if you do not need this degree of control, I would try to keep things simple. Hope this makes sense / helps!

NevilleS commented 8 years ago

With the accounts connection being under the viewer, it's not possible to mutate accounts and so there's a chicken/egg problem there.

Is this actually true? I believe that if your viewer has a global ID (i.e. it's a Node), it will work just fine. Maybe not though, I haven't actually tried it.

dschafer commented 8 years ago

Just ran into similar issues to @ForbesLindesay on a side project I was working on.

My work-around was to create a self-pointer to my root type on my root type:

var rootType = new GraphQLObjectType({
  name: 'Root',
  fields: () => ({
    root: {
      type: new GraphQLNonNull(rootType),
      resolve: () => ({})
    },
    ...restOfRootType
  }),
});

export default new GraphQLSchema({query: rootType});

and then all of my routes use the same RootQuery:

export default {
  root: () => Relay.QL`
    query {
      root
    }
  `,
};

Instead of then creating a deeply nested query to pass to my route, I just create a wrapper component:

function wrapWithC(component) {
  class CWrapper extends React.Component {
    render() {
      return React.createElement(
        component,
        {c: this.props.root.a.b.c},
        this.props.children
      );
    }
  }

  return Relay.createContainer(CWrapper, {
    initialVariables: {
      a: 'a',
      b: 'b',
      c: 'c',
    },
    fragments: {
      root: () => Relay.QL`
        fragment on Root {
          a(aVar: $a) {
            b(bVar: $b) {
              c(cVar: $c) {
                ${component.getFragment('c')}
              }
            }
          }
        }
      `,
    },
  });
}

return <Route
  path={path}
  component={wrapWithC(ComponentThatTakesAC)}
  queries={RootQuery}
/>;

This has ended up working reasonably well for me; I end up not really using the root query functionality to drill down to the object in my schema, and use the wrappers to do that instead.

emmenko commented 8 years ago

@dschafer thanks for sharing! :+1:

dminkovsky commented 8 years ago

The ability to have nesting in root queries per @ForbesLindesay's comment would be great. Would make composition and refactoring easier.

There's no technical reason we couldn't allow nesting in the root query. — @josephsavona

Really? Doesn't there have to be at most one leaf in a root query? Otherwise, how would Relay know at which leaf to anchor fragments?

josephsavona commented 8 years ago

Doesn't there have to be at most one leaf in a root query?

Yes, but Relay could do a recursive search for fragment references (throwing if there is not exactly one of them).

josephsavona commented 8 years ago

Note: We've recently made progress on this with #894 and #895 - these allow root field arguments to be any type including enums and input objects. I've updated the description with next steps.

koistya commented 8 years ago

I wish Relay would support routes like this one (using slugs instead of IDs):

{
  path: '/:username/products/:slug',
  queries: {
    viewer: () => Relay.QL`query { viewer }`,
    product: () => Relay.QL`query { product(username: $username, slug: $slug) }`
  },
  component: Product
}

or, even better this:

{
  path: '/:username/products/:slug',
  query: () => Relay.QL`query {
    viewer,
    product(username: $username, slug: $slug)
  }`,
  component: Product
}

That is supposed to match a URL such as /koistya/products/t-shirt.

If you have a tip for me how to make it work, I would greatly appreciate that!

josephsavona commented 8 years ago

Have you tried using https://github.com/relay-tools/react-router-relay ?

koistya commented 8 years ago

Nope. But I think my problem is not the router itself (a custom one works great for me), but the lack of the support of multiple arguments in root queries. Also, I'd like to find a way to make Realy send a single query request. So I could define my routes as per example above (a single query request with multiple top-level fields in it).

nodkz commented 8 years ago

@koistya somewere Jimmy Jia wrote that to make single request for multiple queries - it is the task of Network Layer. Such NL does not exist yet, but I found https://github.com/eyston/relay-composite-network-layer This is a lit bit different thing, but may be useful as an example for writing such NL.

BTW when connection is root query, you will have different logic with infinite scrolling list. You should pass first arg to router (then it should be passed to query), nor this.props.relay.setVariables({first: incValue}). And I had some problem with sorting (it was problem with multiple arguments in root queries).

So I come back to single root, aka Viewer, and happy again like an elephant.

mattecapu commented 8 years ago

@steveluscher do you know when this will ship?

josephsavona commented 8 years ago

@mattecapu We aren't actively working on this. I'd recommend checking out @dschafer's comment for a good workaround.

mattecapu commented 8 years ago

@josephsavona thank you, can I know why this isn't a priority? I was pretty baffled to discover Relay isn't capable yet to do this kind of things as they seem very natural. As of @dschafer's comment, it looks like a workaround for the multiple-parameters issue, while I need to fetch an array

Comp1 = Relay.createContainer(Comp1, {
    fragments: {
        comp1: () => Relay.QL`
            fragment on Comp1 @relay(plural: true) {
                ${childComp2.getFragment('comp2')}
            }`
    }
});

export const Comp1Queries = {
    comp1: () => Relay.QL`
        query { comp1s(id: $comp1ID) }`
};

But I get

RelayOSSNodeInterface: Expected payload for root field comp1s to be a single non-array result, instead received an array with 1 results.

And I'm not really grasping the viewer workaround.

NevilleS commented 8 years ago

FWIW, since the workaround is so simple (add a "viewer"-esque field on your root query), I'm pretty OK that the Relay team is focusing on other stuff 😁

On Mon, Apr 11, 2016, 10:35 AM Matteo Capucci notifications@github.com wrote:

@josephsavona https://github.com/josephsavona thank you, can I know why this isn't a priority? I was pretty baffled to discover Relay isn't capable yet to do this kind of things as they seem very natural.

— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/facebook/relay/issues/112#issuecomment-208376966

wincent commented 8 years ago

@mattecapu:

can I know why this isn't a priority?

Prioritization is tough, because there are always a lot more things that we would like to do than we have immediate capacity for. Even articulating why decisions are made the way they are can be tough. Perhaps the best way to get insight into what we're working on and why is to take a look at our meeting notes. Let us know if you'd like to know more about any of the things that we're actively working on mentioned in there.

mattecapu commented 8 years ago

@NevilleS the issue is that this thread is perhaps the only documentation of this "feature", so it's a bit disappointing to dive into Relay and then stumble upon this obstacle. Moreover the viewer design pattern is not explained anywhere (at least I can't find an explanation, I'll be happy to be wrong on this!) and as a beginner I scratched my head looking at the example and thinking "Why this?". In fact I'd be happy to understand how to work around this for now.

@wincent I may sound pedant/unrespectful but actually I'm a great fan of the whole React/Relay/GraphQL thing and I understand the last two are still a pretty novel technology so it's fair to have some issues! I want you (and the rest of the team) to know I really appreciate your work and effort in doing this! OSS is hard and you're doing fine anyway :smile: The question was more a matter of understanding, because the queries I wrote seemed straightforward to me and clashing with this issue made me wonder if I really understood how Relay is supposed to work or I'm missing something.

mattecapu commented 8 years ago

Okay building on @dschafer's solution I managed to work around this. For now is just an ad-hock hack but I hope to encapsulate this logic in a general-purpose wrapper.

I edited the code above in the following way:

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    description: 'Root query',
    fields: () => ({
        // HACK to make queries at root level
        rootHack: {
            type: new GraphQLNonNull(RootQuery),
            resolve: () => ({})
        },
        ...restOfRootType
});
// ...
export default new GraphQLSchema({
    query: RootQuery
});
class Comp1 extends React.Component {
    render() {
        // comp1s, in my case, is an array (its type is GraphQLList(comp1))
        return (
             <Comp2 comp1={this.props.comp1query.comp1s} />
        );
    }
}

Comp1 = Relay.createContainer(Comp1, {
    initialVariables: {
        // this is then changed by react-router based on a URL parameter with the same name
        comp1ID: 0
    },
    fragments: {
        comp1query: () => Relay.QL`
            fragment on RootQuery {
                comp1s(id: $comp1ID) {
                    ${childComp2.getFragment('comp2')}
                }
            }`
    }
});

// this will be the same for every page
const Comp1Queries = {
    comp1: () => Relay.QL`
        query { rootHack }`
};

My react-router routes

<Router>
        <Route path={"/comp1/:comp1ID"}
            component={comp1} queries={Comp1Queries}
            prepareParams={({ comp1ID }) => ({ comp1ID: parseInt(comp1ID) })} />
</Router>

Hope to help someone with this

jardakotesovec commented 8 years ago

Do I understand correctly that this issues covers just root fields? I was wondering if there are any existing or planned options how to tell relay that some argument is identifying or that its the graphQL id. Therefore Relay could just fetch additional data.

It would make fetching some additional information very convenient, because if I want get (on demand) some additional information in some component I need to do it in fragment. I don't see how I could make it with root fields which are always defined along with root container&component.

josephsavona commented 8 years ago

any existing or planned options how to tell relay that some argument is identifying or that its the graphQL id

Can you clarify how this would be different than node?

It would make fetching some additional information very convenient,

Hmm. Can you clarify your goal here? There are two options for fetching data in a component, whichever is more convenient: use setVariables/forceFetch or manually construct a query and fetch it. The second option is easy to overlook, but you can always create a root query without a route and fetch it:

const query = Relay.createQuery(Relay.QL`query Foo { ... }`, variables); 
RelayStore.primeCache({query}, readyState => {
  if (readyState.error) {
    console.error(readyState.error);
  } else if (readyState.done) {
    // data is ready
  }
});

Note that this method will write the results of the query into the cache, and all affected components will re-render if necessary (i.e. you don't need to call setState or anything in the callback above).

jardakotesovec commented 8 years ago

@josephsavona I apologize for vague description what I had in mind. Example should clear it up.

export default Relay.createContainer(ComponentA, {
    initialVariables: {
        formatIds: [],
    },
    fragments: {
        viewer: () => Relay.QL`
            fragment on Viewer {
                formats (ids: $formatIds) {
                    id
                    name
                    code
                }
            }
        `
    }
})

This is the on demand scenario when I want to get metadata for formats using setVariables. My use case is that I have some general informations for all formats (from initial 'static' query) but need to get some extra informations on demand on different places in the app and wanted to reduce over-fetching. So I know the ids for formats and want to get extra fields on-demand for few of them.

But afaik it is not possible to tell Relay that I am gonna use list of actual graphQL ids. Therefore it re-fetches format for any new combination of ids.

So lets say at first I would do: 1) setVariables({formatIds: [id1, id2]}), relay fetches formats id1 and id2 with all required fields in this fragment instead of just missing fields. 2) setVariables({formatIds: [id3, id2]}), relay fetches formats with id3 and id2 instead of just missing fields for id3

node field is the closest thing, but I can't use it for list of ids and use it in component fragment to get data from container.

Globegitter commented 8 years ago

It might even be nice to have a concept of nodes() field which would require either a type or ids param but also would allow for some additional optional filters so this could be used for fetching all nodes of the given type, fetch all nodes given the list of ids and have some additional customer filters (e.g. I want only nodes of a type that include a title)

Looking something like this:

const nodesQueryConfig = {
  queries: {
    nodes: () => {
      return Relay.QL`query { nodes(ids: $ids) }`
    }
  },
  name: 'nodesQueryConfig',
  params: {
    ids: [id1, id2]
  }
};

Or of course wrapped in the viewer.

Globegitter commented 8 years ago

aahh @jardakotesovec have you seen the @relay(plural: true) option yet? That would allow you to ask for multiple ids and return you all these elements.

josephsavona commented 8 years ago

node field is the closest thing, but I can't use it for list of ids and use it in component fragment to get data from container.

Relay supports plural identifying root fields which work exactly as you're describing. If you implement a nodes(ids: [ID!]): [Node] field in the schema, Relay understands that each of the id arguments corresponds to the specific Node with that id. If you fetch nodes(ids: [1,2]) and then nodes(ids: [2,3]), Relay will know that id 2 has already been fetched, and diff this to only fetch node(id: 3).

kamek-pf commented 8 years ago

@josephsavona

Note that this method will write the results of the query into the cache, and all affected components will re-render if necessary (i.e. you don't need to call setState or anything in the callback above).

I actually tried that, I built a query to fetch some additional fields on a node and sent it to the node interface. The query built by Relay looks correct (only the missing fields are asked), but the result is never merged into the cache. The only way I found to get the updated data is Relay.Store.readQuery(query)[0]; Any idea what I'm doing wrong ?

josephsavona commented 8 years ago

@kamek-pf the public data-fetching APIs always merge data into the cache. If you're not seeing an update one of two things could be happening:

Either way, can you open a new issue for this? This is tangential to the OP.

jardakotesovec commented 8 years ago

@josephsavona I am aware of the plural identifying root field, but I have two issues there

LegNeato commented 8 years ago

So, with the viewer workaround, how do we query the node root field if everything goes through viewer? Is this what the additional "self-pointer to my root type" workaround is used for? So you can do something like:

fragment on Viewer {
  root {
    node(id: $whatever) {
    ...
  }
}

But doing that gives me:

You defined a `node(id: ID!)` field on type `Query`, but Relay requires the `node` field to be defined on the root type.
mjtamlyn commented 8 years ago

@LegNeato The node field needs to be on Query, alongside your root node.

LegNeato commented 8 years ago

I have it there but still not getting how to query it from client code as the client fragments all refer to viewer.

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    node: nodeField,
    viewer: {
      type: viewerType,
    },
  }),
});

I'll poke at it some more and take it to stackoverflow if I still don't get it.

miracle2k commented 8 years ago

@LegNeato I used to have my own copy of node called object() below of client. I believe the reason I named it "object" is the error you posted above. The check for the location of "node" that Relay does is, I think, to dumb to understand that this is an additional node field. Try to rename it.

However, have you considered (I am doing this now instead myself) just using two root queries:

export const relayRoute = (queries, paramsGetter) => BaseComponent => {
  // Generate a route
  class GeneratedRoute extends Relay.Route {
    static queries = queries;
    static paramDefinitions = {};
    static routeName = 'GeneratedRoute';
  }

  // Return a HOC that injects a route generated by routeGetter.
  return withProps(ownerProps => {
    // Let user's function generate the route
    const params = paramsGetter(ownerProps);
    return {relayRoute: new GeneratedRoute(params)};
  })(BaseComponent);
}
const route = relayRoute({
client: () => Relay.QL`
    query { client }
`,
menu: () => Relay.QL`
    query { menu: node(id: $id) }
`
})
LegNeato commented 8 years ago

I ended up figuring it out, or at least something that works nicely. @miracle2k I think I can't just blindly use $id as some routes have multiple nested ids. Honestly didn't try it though.

In the schema definition:

// This is top-level / "root" node.
const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    // The node query lets us query for any object with an id w/o having to manually define
    // queries under the viewer "global node". 
    node: nodeField,
   // This is the "global node" workaround mentioned above.
    viewer: {
      type: viewerType,
    },
  }),
});

export const Schema = new GraphQLSchema({
  query: queryType,
});

In the react-router definitions:


// This means when BarComponent is rendered, it will
// do a node query with the barID from the URL and query the node fields defined in
// the BarComponent component fragment named "node".
<Route path=":fooID/edit/:barID" component={BarComponent} queries={{
    viewer: () => Relay.QL`query { viewer }`,
    node: () => Relay.QL`query { node(id: $barID) }`,
 }} />

In the client component:

export default Relay.createContainer(BarComponent, {
  fragments: {
    // Define what we need to query from the viewer "global node".
    viewer: () => Relay.QL`
      fragment on Viewer {
        // Whatever fields...
      }
    `,
    // Define what we need to query from the node.
    node: () => Relay.QL`
      fragment on Node {
        id,
        ... on Bar {
            // Whatever fields...
        }
      },
    `,
  },
});
wincent commented 8 years ago

We're preparing Relay 2, which completely removes the restrictions and special casing around root fields. So I'm going to close this as we're unlikely to take any direct action on it, but people using Relay 1 will still be able to find this issue via search. Thanks to everybody who has participated in the thread!

josephsavona commented 8 years ago

@maletor That extra level of indirection isn't necessary. It's sufficient to do:

query {
  viewer {
    rootConnection
    multipleArgs(a: 1, b: 2)
  }
}
ermik commented 6 years ago

@wincent I ran into an issue recently where createRefetchContainer refetching a root query prop wouldn't subscribe to the store. The refetch() calls would use the component configuration fragment and the query correctly, successfully refetching data, but the component would never get initial query props nor would they update on refetch. Tests in a dedicated <QueryRenderer /> as well as in a more complex component tree has shown this to be an issue. But once the query field was moved on a second level (e.g. viewer or anything else) — the component behaves as expected.

Problematic code:

{
    query: graphql`
      fragment RelaySelectComponent_query on Query
        @argumentDefinitions(s: { type: "String", defaultValue: "beer" }) {
        products(search: $s) {
          edges {
            node {
              id
              gtin
              description
            }
          }
        }
      }
    `
  },
  graphql`
    # Refetch query to be fetched upon calling refetch.
    # Notice that we re-use our fragment and the shape of this query matches our fragment spec. $count: Int,
    query RelaySelectComponentRefetchQuery($s: String = "wine") {
      ...RelaySelectComponent_query @arguments(s: $s)
    }
}

I am not as experienced as one (i.e. me) would hope, so I can't make it into a definitive bug report, but I'm asking, respectfully, you guys take a look. I wasted good 20 work hours trying to find a solution.

ermik commented 6 years ago

@yungsters maybe you can comment on this?

sibelius commented 6 years ago

This is working fine on Relay Modern