Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?

2015-01-29 Unofficial Relay FAQ

Compilation of questions and answers about Relay from React.js Conf.

Disclaimer: I work on Relay at Facebook. Relay is a complex system on which we're iterating aggressively. I'll do my best here to provide accurate, useful answers, but the details are subject to change. I may also be wrong. Feedback and additional questions are welcome.

Table of Contents

What is Relay?

Relay is a new framework from Facebook that provides data-fetching functionality for React applications. It was announced at React.js Conf (January 2015).

Each component specifies its own data dependencies declaratively using a query language called GraphQL. The data are made available to the component via properties on this.props.

Developers compose these React components naturally, and Relay takes care of composing the data queries into efficient batches, providing each component with exactly the data that it requested (and no more), updating those components when the data changes, and maintaining a client-side store (cache) of all data.

What is GraphQL?

GraphQL is a data querying language designed to describe the complex, nested data dependencies of modern applications. It's been in production use in Facebook's native apps for several years.

GraphQL itself is an engine for mapping from queries to code that is responsible for actually fetching the data, so is agnostic about what underlying storage is actually used. Relay uses GraphQL as its query language, but it is not tied to a specific implementation of GraphQL.

What's the value proposition?

By co-locating the queries with the view code, the developer can reason about what a component is doing by looking at it in isolation; it's not necessary to consider the context where the component was rendered in order to understand it. Components can be moved anywhere in a render hierarchy without having to apply a cascade of modifications to parent components or to the server code which prepares the data payload.

Co-location leads developers to fall into the "pit of success", because they get exactly the data they asked for and the data they asked for is explicitly defined right next to where it is used. This means that performance becomes the default (it becomes much harder to accidentally over-fetch), and components are more robust (under-fetching is also less likely for the same reason, so components won't try to render missing data and blow up at runtime).

Relay provides a predictable environment for developers by maintaining an invariant: a component won't be rendered until all the data it requested is available. Additionally, queries are defined statically (ie. we can extract queries from a component tree before rendering) and the GraphQL schema provides an authoritative description of what queries are valid, so we can validate queries early and fail fast when the developer makes a mistake.

The other thing Relay does to prevent errors is "masking" of the data it passes into each component. This means that only the fields of an object that a component explicitly asks for will be accessible to that component, even if other fields are known and cached in the store (because another component requested them). We call this masking, and it makes it impossible for implicit data dependency bugs to exist latently in the system.

Note that co-location in itself isn't the end goal here. At the moment our queries reside, explicitly, in the components, but through the power of static analysis (specifically, Flow and the type information encoded in the GraphQL schema), you can imagine a state where the queries can be inferred via an analysis of which subcomponents a component renders (and therefore, which subqueries need to be composed into the component query) and which properties it itself accesses. If we can get there, then over-fetching and under-fetching will go from "unlikely" to outright impossible.

By handling all data-fetching via a single abstraction, we're able to handle a bunch of things that would otherwise have to be dealt with repeatedly and pervasively across the application:

  • Performance: All queries flow through the framework code, where things that would otherwise be inefficient "N+1" query patterns get automatically collapsed and batched into efficient, minimal queries. Likewise, the framework knows which data have been previously requested, or for which requests are currently "in flight", so queries can be automatically de-duplicated and the minimal queries can be produced.
  • Subscriptions: All data flows into a single store, and all reads from the store are via the framework, so the framework knows which components care about which data and should be re-rendered when data changes; components never have to set up individual subscriptions.
  • Common patterns: We can make common patterns such as pagination easy (this is the example that Jing gave at the conference); if you have 10 records initially, getting the next page just means declaring you want 15 records in total, and the framework automatically constructs the minimal query to grab the delta between what you have and what you need, requests it, and re-renders your view when the data becomes available.
  • Simplified server implementation: Rather than having a proliferation of end-points (per action, per route), a single GraphQL endpoint can serve as a facade for any number of underlying resources.
  • Uniform mutations: There is one consistent pattern for performing mutations (writes), and it is conceptually baked into the data querying model itself. You can think of a mutation as a query with side-effects: you provide some parameters that describe the change to be made (eg. attaching a comment to a record) and a query that specifies the data you'll need to update your view of the world after the mutation completes (eg. the comment count on the record), and the data flows through the system using the normal flow. We can do an immediate "optimistic" update on the client (ie. update the view under the assumption that the write will succeed), and finally commit it or roll it back in the event of an error when the server payload comes back.

How does it relate to Flux?

In some ways Relay is inspired by Flux, but the mental model is much simpler. Instead of multiple stores, there is one central store that caches all GraphQL data. Instead of explicit subscriptions, the framework itself can track which data each component requests, and which components should be updated whenever the data change. Instead of actions, modifications take the form of "mutations".

There may be use cases where you let Relay manage the bulk of the data flow for your application, but use a Flux store on the side to handle a subset of application state. For example: with Relay, you may be able to model all of your workflow in terms of GraphQL queries (for reading data) and mutations (for writing data). But there may be times where you have "draft" mutations that you want to build up over time on the client (for example, think of a "wizard" work flow) and for some reason you don't want to persist this state to the server or use a real mutation; you could manage these using Flux (and store it ephemerally in an in-memory data structure, or in local storage). We may eventually end up baking some of these workflows into Relay itself, but in the meantime, nothing will prevent you from blending it with other approaches.

What about routing?

Relay does have a notion of routes and routing, but it's one of the APIs that we're currently improving so I'll keep away from details (which may change) and try to speak in generalities.

Relay uses routes to determine which data to fetch to render a given component (it's possible for a component to be composed on any number of different views).

The data required to render a particular view is a function of the route and any query params that may be supplied in the route or by the component itself.

You can think of the route as a URI, which itself may contain "query params" (not necessarily part of a URI query string; the params may be embedded as path components with the URI).

Does Relay routing aim to replace something like react-router, or both will be complementary?

Right now we have our own routing. We've been talking to the react-router people to see if there's a way we can make things work together.

How does Relay handle rendering during data fetches?

You said that an invariant was that a component won't be rendered until the data it requested is available. How would I render things like placeholders and loading indicators in this case?

Relay allows you to mark part of a query as "deferred". Anything which is not marked as deferred is considered to be required. Relay won't render a component until all of its required data is available. We provide an API for components to check whether their deferred data is available, missing, or on the way. Using these primitives, you can build interfaces which do things like immediately show their navigation and core content, and subsequently load in comments, while showing a loading indicator in the meantime.

How does GraphQL work?

Each GraphQL query begins with a "root call" which allows us to start at a particular node, or set of nodes, in the graph. From there, "fields" in the query describe what information we want on these objects, as well as the fields we want on the subsequent arbitrarily nested objects.

The backbone of the GraphQL engine is a schema, created from definition classes that contain rich metadata, which provide two things: (1) a description of all the possible fields, relationships and types that can be represented in a valid query; and (2) the mapping from fields to the actual retrieval mechanism. These classes can be used to wrap business objects, or services, or any other data source we wish to expose to GraphQL.

The GraphQL engine parses queries into an AST representation and given this tree and the schema, it traverses the nodes evaluating an executor which uses the definitions from the schema to retrieve objects, and access fields on the retrieved objects (for example, a field may map to a property on the object, or to a function that computes derived data or itself performs an arbitrary call to another service).

This is made performant by a combination of two things: pervasive caching (so that if the same data is requested multiple times at different places in the tree, the actual data is fetched only once) and extensive use of asynchronous primitives (async/await) which enable us to effectively parallelize and batch operations.

How does GraphQL know what type of data is being requested by a node() root call?

In the video's example, they said they use a node(id) root call, but it's not clear how to determine the model for a given id.

That's all going to be implementation-specific. For example, at Facebook the two root calls that we most commonly use are:

  • viewer(): specifies an object representing the current viewer; and you can imagine a query that looks something like viewer() {news_feed.first(10) { ... }}
  • node(): returns an object from the Facebook Graph specified by a Facebook ID; this could actually be any of a number of things (such as a page, or a person) that conform to the Node interface
So, in the case of the node() root call, we know from the GraphQL schema what the possible types are, but we don't know the actual type of the object until we start accessing it. Thanks to the schema, we know what fields are valid for any given type, so we return only the fields that make sense for the specific object in question.

Another thing to note is that IDs passed to the node() root call don't have to be integers, they can be opaque strings that map to pretty much anything you want, so there is considerable flexibility for mapping GraphQL concepts onto those from existing storage systems.

What will you be releasing for doing GraphQL on the server side?

We'll be releasing a spec describing GraphQL in detail and a reference implementation of the GraphQL engine. Note that the engine itself doesn't actually fetch any data; it will rely on some kind of adapter layer to do that, and there a lots of possibilities there for doing this in different languages, talking to caches, services, databases, ORMs, and embedding business logic etc. We intend to provide examples showing what kinds of patterns are possible.

Would it be possible to implement the compatible GraphQL endpoint in an arbitrary language?

Yes, absolutely. The grammar for GraphQL is relatively tiny, and the engine itself (parser, executor) is small. We'll be releasing a reference implementation, which I fully expect will be able to be ported to other languages in a straightforward manner.

How is GraphQL syntax different from JSX?

Syntax in GraphQL looks different from JSX (eg. ${name} instead of {name}). Is that intentional or accidental?

These are ES6 template literals, which we use because they're familiar to ES6 developers. The two main places where you'll see this kind of interpolation are when inserting query params (eg. friends.first(${params.count})) and when composing queries from subcomponents (eg. ${ProfilePicture.getQuery('viewer')}).

There is actually some additional magic going on here — our transform pipeline actually embeds the AST directly (as JavaScript objects) in place of the template — but generally developers won't need to worry about that at all.

Why aren't query params component state?

Jing's slide showed us modifying query params using a this.setQueryParams call, rather than React's this.setState.

This is because setting query params is an inherently async operation. Setting query params may trigger a network request. Multiple setQueryParams calls may be issued before the results of prior calls arrive. Later calls should supersede prior calls. The corresponding fetchs may fail or need to be retried. This complexity needs to be abstracted away.

The framework preserves an invariant that it won't try to render a component until all the data it requested are available, so it wouldn't do to only have, say, 10 objects in a list and to trigger a render when the query params have been updated to request 15 objects but the next 5 objects haven't arrived yet.

So, the setQueryParams API provides us with an abstraction behind which to hide the details of all this asynchrony. The framework can track both "current" and "pending" values of query params, and make sure that the component always sees the right value for any given query param (ie. the one that reflects the reality of what we have in the store at render time).

This is an API that we're actively working on right now, so it may change between now and the open source release.

Where is Relay being used?

Relay is being used in a few places in production. It's being used with React Native in the Facebook Groups app (currently on the iOS app store) and on an experimental new version of the Facebook mobile website that is currently rolled out to a small number of test users.

Does Relay support "isomorphic" JS?

Isomorphism is indeed a core part of Relay, mostly thanks to React's isomorphism. Relay's server rendering mode works very similarly to other solutions for React, where it renders to a string instead of DOM and then the client runs React again to inflate the component tree. Since all data fetching happens through the framework, the ability to capture all of the fetched data and ship it to the client to prime the client's store is pretty straightforward.

Since the framework can statically build the GraphQL query without rendering, it's also possible to preload the store in the initial server response, but still render on the client. This is really cool because it parallelizes data fetching and bootstrapping the page.

In addition to these "server" and "preload" modes, we also have a "client" mode in which literally everything is performed on the client (building the query, fetching the data, doing the initial render); this can be very useful for debugging purposes.

Does Relay make use of immutable data structures?

Relay currently provides a default implementation of shouldComponentUpdate which is aware of all the Relay-managed data flow and can short-circuit updates when the data haven't changed. Immutability as a general concept is still important (to get those cheap === comparisons) so we try to leverage that as much as possible internally, although there is still more that we can do there.

Will Relay be compatible with standard RESTful APIs?

Relay depends on GraphQL as the query language and assumes your app will talk to an endpoint that speaks GraphQL, but the underlying implementation of the endpoint can be anything you want (including talking to other services which may or may not be RESTful). This means that at the application layer you are no longer thinking in terms of individual "resources" but rather the entire hierarchy of data that your application is going to need (ie. not resources, but trees of resources).

Each component specifies the bit of data that it will need in the form of a query fragment, and the framework takes care of composing all of the fragments into a larger hierarchy that represents the entire query. Because all of this is centralized in the framework, even dealing with massive queries (ie. all the data your entire application needs to render a complex, nested view hierarchy) can be made efficient through caching, batching, re-use, and other means to reduce the size of queries.

When can we have it?

We're working very hard right now to get this ready for public consumption and we are super excited about sharing it with you, but we can't say yet when that will be. We'll keep you posted!

How can we learn more in the meantime?

You can expect to see blog posts and other materials from us with more details.

@tindli
tindli commented Jan 29, 2015

Awesome thanks. I have 2 questions:

  1. Any words about using it server side for initial page rendering; is it a core part of Relay, preload of the store etc.?
  2. Does this somehow reduce the need for immutable data structures; accidental overwrites, efficient shouldComponentUpdate()?
@pspeter3

Is there more documentation on mutations somewhere?

@mroch
mroch commented Jan 29, 2015

@tindli: isomorphism is indeed a core part of Relay, mostly thanks to React's isomorphism. Relay's server rendering mode works very similarly to other solutions for React, where it renders to a string instead of DOM and then the client runs React again to inflate the component tree. Since all data fetching happens through the framework, the ability to capture all of the fetched data and ship it to the client to prime the client's store is pretty straightforward.

Since the framework can statically build the GraphQL query without rendering, it's also possible to preload the store in the initial server response, but still render on the client. This is really cool because it parallelizes data fetching and bootstrapping the page.

@uberllama

Thanks for the details. Will Relay be compatible with standard RESTful APIs? Does it have a preference for synchronous side-loaded association data or async association data?

@SimonDegraeve

Thanks for the complementary informations... Just two things I didn't get from the conf:

  1. How GraphQL knows what type of data is the root. In the video's example, they said they transform the root into node(id) but no clue how to define the root model and the id.

  2. On server side for GraphQL do you plan to release a parser GraphQL -> SQL or an ORM or nothing, or... ?

Any chance we get an early preview on github of Relay/GraphQL so we can start thinking the "Relay way" for our new apps?

@SimonDegraeve

I forgot ;-)

  1. Does Relay routing aim to replace something like react-router, or both will be complementary?
@abdullin

Thank you for all the information. I have a few questions as well.

  1. Is there an AST definition for GraphQL? Would it be possible to implement the compatible endpoint in an arbitrary language?
  2. Syntax in GraphQL looks different from JSX (e.g.: ${name} instead of {name}). Is that intentional or accidental?
@SimonDegraeve

@abdullin

  1. The syntax is from template string so it is standard ES6. And they use a tag function (graphql) to parse the string and merge the different queries

It is similar to coffeescript string interpolation sentence = "#{ 22 / 7 } is a decent approximation of π", without the tag method.

@ryanzec
ryanzec commented Jan 30, 2015

Is Relay only going to be usable with a GraphQL style endpoint? What if you have an existing REST API where you have different endpoints for different resources and the such, can you still use Relay with that type of REST API?

@wincent
Owner
wincent commented Jan 30, 2015

Thanks for the questions. I'll answer what I can inline here and update the gist. I'll also roll in @mroch's answer.

@tindli:

  1. Does this somehow reduce the need for immutable data structures; accidental overwrites, efficient shouldComponentUpdate()?

Relay currently provides a default implementation of shouldComponentUpdate which is aware of all the Relay-managed data flow and can short-circuit updates when the data haven't changed. Immutability as a general concept is still important (to get those cheap === comparisons) so we try to leverage that as much as possible internally, although there is still more that we can do there.

@uberllama:

Will Relay be compatible with standard RESTful APIs? Does it have a preference for synchronous side-loaded association data or async association data?

Relay depends on GraphQL as the query language and assumes your app will talk to an endpoint that speaks GraphQL, but the underlying implementation of the endpoint can be anything you want (including talking to other services which may or may not be RESTful). This means that at the application layer you are no longer thinking in terms of individual "resources" but rather the entire hierarchy of data that your application is going to need (ie. not resources, but trees of resources).

Each component specifies the bit of data that it will need in the form of a query fragment, and the framework takes care of composing all of the fragments into a larger hierarchy that represents the entire query. Because all of this is centralized in the framework, even dealing with massive queries (ie. all the data your entire application needs to render a complex, nested view hierarchy) can be made efficient through caching, batching, re-use, and other means to reduce the size of queries.

@SimonDegraeve

  1. How GraphQL knows what type of data is the root. In the video's example, they said they transform the root into node(id) but no clue how to define the root model and the id.

That's all going to be implementation-specific. For example, at Facebook the two root calls that we most commonly use are:

  • viewer(): specifies an object representing the current viewer; and you can imagine a query that looks something like viewer() {news_feed.first(10) { ... }}
  • node(): returns an object from the Facebook Graph specified by a Facebook ID; this could actually be any of a number of things (such as a page, or a person) that conform to the Node interface

So, in the case of the node root call, we know from the GraphQL schema what the possible types are, but we don't know the actual type of the object until we start accessing it. Thanks to the schema, we know what fields are valid for any given type, so we return only the fields that make sense for the specific object in question.

Another thing to note is that IDs passed to the node() root call don't have to be integers, they can be opaque strings that map to pretty much anything you want, so there is considerable flexibility for mapping GraphQL concepts onto those from existing storage systems.

  1. On server side for GraphQL do you plan to release a parser GraphQL -> SQL or an ORM or nothing, or... ?

We'll be releasing a spec describing GraphQL in detail and a reference implementation of the GraphQL engine. Note that the engine itself doesn't actually fetch any data; it will rely on some kind of adapter layer to do that, and there a lots of possibilities there for doing this in different languages, talking to caches, services, databases, ORMs, and embedding business logic etc. We intend to provide examples showing what kinds of patterns are possible.

Any chance we get an early preview on github of Relay/GraphQL so we can start thinking the "Relay way" for our new apps?

You can expect to see blog posts and other materials from us with more details. We don't have a timeline for when we'll be sharing actual code, but we're working now on getting things ready.

  1. Does Relay routing aim to replace something like react-router, or both will be complementary?

Right now we have our own routing. We've been talking to the react-router people to see if there's a way we can make things work together.

@abdullin

Is there an AST definition for GraphQL? Would it be possible to implement the compatible endpoint in an arbitrary language?

Yes, absolutely. The grammar for GraphQL is relatively tiny, and the engine itself (parser, executor) is small. We'll be releasing a reference implementation, which I fully expect will be able to be ported to other languages in a straightforward manner.

Syntax in GraphQL looks different from JSX (e.g.: ${name} instead of {name}). Is that intentional or accidental?

This is because of our use of ES6 template literals.

@SimonDegraeve

  1. The syntax is from template string so it is standard ES6. And they use a tag function (graphql) to parse the string and merge the different queries

We use template literals because they're familiar to ES6 developers, but there is actually some additional magic going on here: our transform pipeline actually embeds the AST directly (as JavaScript objects) in place of the template.

@matthewmueller

@wincent, thanks for putting this together. GraphQL looks great, I have a question about the implementation:

As someone querying the backend, how would I know if I'm getting back a single object or a collection of objects? From the example in the talk:

node(123) {
  birthday: {
    month,
    day
  },
  friends: {
    cursor,
    node {
       name
    }
  }  
}

How do we know that birthday is going to return an object, while friends is going to return an array of objects? Also, is there querying support on arrays that aren't an array of objects (ex. fruits: ['apple', 'pear', 'grape']?

@SimonDegraeve

@wincent thank you for the explanations.

node(): returns an object from the Facebook Graph specified by a Facebook ID; this could actually be any of a number of things (such as a page, or a person) that conform to the Node interface

Ok, I thought node was like a keyword from the GraphQL syntax, but if I understand it is instead a reference to a kind of model. So is cursor? Not a keyword but something depending from the GraphQL endpoint? So it means only calls (ex: first(), after(), etc) are syntax specific?

One more question, is there any way to specify some where clause for a query in GraphQL?

Also I just discovered Haxl and it looks like it could be a part of what Relay uses to optimises the request, looking through the repo helped me to understand the GraphQL endpoint and the adapters concepts. Maybe it is worth mentioning it somewhere in this doc.

@jeffchan
jeffchan commented Feb 1, 2015

@wincent this is awesome!

Can you give an example of what a mutation query looks like in GraphQL?

@elierotenberg

Is there/will be a part (or extension?) of GraphQL/Relay that may enable the server to actively push updates to the subscribers of given resources?
Related to Flux over the Wire.

@cpojer
cpojer commented Feb 1, 2015

@SimonDegraeve Every object that has an id field must be uniquely identifiable by that id. node(<id>) allows you to query for that specific object. Connections (friends, comments) have edges with a cursor and a node, where the cursor allows you to navigate within that connection ( friends.after(<cursor>).first(10) for example) and node is the actual object.

Example query fragment:
friends.first(10) { edges { cursor, node { id, name } } }
Example response:
{friends: { edges: [ { cursor: <someToken> node: {id: <id1>, name: "SimonDegraeve"} }, { cursor: <someOtherToken> node: {id: <id2>, name: "wincent"} } ] }}

@jeffchan Simple explanation: you give the server a query and a payload and they make up a mutation. Example: the create user mutation could be CreateUserMutation { name, password } with a JSON payload similar to {name: "Jeff Chan", password: "123456"} and the server will then do the right thing and give you back a new user object that you can feed into the store (and a potential optimistic update can put the user in the store before the request to the server)

@elierotenberg That is certainly on our minds but I can't really comment on that at this stage

@cpojer
cpojer commented Feb 1, 2015

@MatthewMueller GraphQL knows it is a connection because GraphQL has a schema.

@jimthedev

Are there any special considerations for working on offline or occasionally offline applications using Relay? Sounds like there is a cache, but will the components only know that they are getting data. For occasionally offline info it may be useful to let users know that a component may be stale. Is this outside of the scope of GraphQL?

@cesarandreu
  1. Could you use websockets along with this to subscribe / unsubscribe automatically to cached events in order to receive pushed updates? For example, this could be really useful when implementing something like a chat client.
  2. Do components try to get rendered as quickly as possible with cached data, and then update themselves once the data store has been updated? And if this is the case, how would you handle something like a form? If you had a component that displayed some resource information, and it had an edit panel. Would you request the latest data before rendering the edit panel or use whatever data was loaded to display the resource? If you allow the server to push updates I'm guessing you would keep a copy of the form data in the component state, which you use to render the form, and upon receiving an update you display a notification. Is that how you'd handle such a scenario?
@taion
taion commented Feb 1, 2015

I'm also curious about how this works for streaming data getting pushed to the client in real time. Is this something that Facebook handle using Relay/GraphQL, or is that something separate?

@onesupercoder

Awesome!!!

@aakashbapna

For isomorphic JS, you use node.js for executing JS on server side?

@davidaurelio

@wincent, thank you for the extensive overview.

I have two questions about GraphQL:

  • Will it be possible to declare properties as optional? That means signaling to the data provider that certain parts of the query are not necessary for initial rendering and can be provided later or not at all?
  • Will GraphQL support conditional dependencies, i.e. properties that depend of the value of other properties? Or would that be considered an anti-pattern that is supposed to be resolved with means of data hierarchy/nesting?
@rmosolgo
rmosolgo commented Feb 1, 2015

@cpojer Thanks for the example of the mutation query. Let me see if I understand right:

  • the graphQL server would define mutations (a whitelist of possible actions, along with their implementations)
  • the client's request includes 1) which mutation to perform 2) required data for that mutation

Seems like action/query level is the right place to perform authentication in this kind of architecture, huh?

@eyston
eyston commented Feb 2, 2015

This is an incredibly helpful writeup. I've been playing around with GraphQL all weekend and this has made it make so much more sense. I kept putting some kind of technical connotation on 'Viewer' as like some query-view-thing when it instead just means 'the current user, you know, the one viewing the page -- viewer'.

One issue I'm having is getting a handle on the example of mutual_friends {count} in the video example. Is mutual friends a one-to-many or just a nested component that doesn't actually have edges to the actual friend nodes? If it is a one-to-many I'm not sure how to square it with the other examples -- it has no first and I don't know where in the structure of the response count could be placed.

This also relates to @cpojer 's explanation of how to query edges. In his comment it looks like:

friends.first(10) { edges { cursor, node { id, name } } }

which results in

{friends: { edges: [
    { cursor: <someToken> node: {id: <id1>, name: "SimonDegraeve"} },
    { cursor: <someOtherToken> node: {id: <id2>, name: "wincent"} }
] }}

So in this shape I could see how you could slip a count in at the same depth as edges. But in the video we see this shape instead:

friends.first(1) { cursor, node { name } }

and

{friends: [{
    { cursor: <someToken> node: {id: <id1>, name: "SimonDegraeve"} },
    { cursor: <someOtherToken> node: {id: <id2>, name: "wincent"} }
] }}

No edges. I'm not sure where you'd put a count in that.

Last ... is it a stupid idea for the cursor and id to be the same if your datastore doesn't have some kind of cursoring built in? In all of the examples the cursor seems to be a way to say 'starting from this point, take 5 more' which means an id could be sufficient (e.g. the id is the 15th result in some sorted index so we need 16-20).

@becomingGuru

Looks awesome. Trees of resources for the app to function rather than resources managed by the Developer.

@dschafer
dschafer commented Feb 2, 2015

But in the video we see this shape instead:
friends.first(1) { cursor, node { name } }

Good eye! @cpojer's query is correct, there should be an extra layer of indirection in there; in GraphQL as it exists today, that query would more correctly be

friends.first(1) { edges { cursor, node { name } } }

I omitted the edges layer in the slides to simplify the example. As you note, the reason for that layer of indirection is so that edges and count can both be fields on the connection object, but I knew I wasn't going to have time to cover that in the talk. Since without that explanation, edges looks like an unnecessary layer, I removed it in my slides. Good catch!

One issue I'm having is getting a handle on the example of mutual_friends {count} in the video example. Is mutual friends a one-to-many or just a nested component that doesn't actually have edges to the actual friend nodes? If it is a one-to-many I'm not sure how to square it with the other examples -- it has no first and I don't know where in the structure of the response count could be placed.

mutual_friends was intended to be (and in our schema, is) a connection as well; it would return an object with count and edges fields, and edges would be the field that returned a list. One tricky aspect of connections in GraphQL is how count works (because it's inside the connection, but what do pagination calls do to count?). Even internally it's not well understood, and I'm hoping that one of the results of our efforts to define a spec will be a clarification of its behavior.

Last ... is it a stupid idea for the cursor and id to be the same if your datastore doesn't have some kind of cursoring built in?

Cursors are designed to be opaque; you can put whatever information in them you require to do pagination. Depending on the backend, we use IDs, or timestamps, or a combination of data. So if IDs are the best piece of information to allow you to paginate, go for it!

@zoomclub
zoomclub commented Feb 3, 2015

Would GraphQL and Relay work with Firebase? Seems similar to Firebase in some ways...

@jbmusso
jbmusso commented Feb 4, 2015

Very interesting read, thanks. GraphQL would fit very well with Gremlin, a graph processing language for graph databases provided by the Apache TinkerPop stack. React+GraphQL+Gremlin would be very powerful and I'll certainly try to provide an implementation for this.

@travischoma

Very interested in GraphQL and Relay. Was wondering if there is/will be support for real time apps, in the sense that a component could 'subscribe' to changes of it's data dependencies and changes would be pushed from the server?

@mindjuice

What about security/authorization issues? Having clients be able to just request whatever data they want without regard to roles and permissions is obviously not good. How does Relay/GraphQL handle this?

@CoryDanielson

Few questions all related. I've read through the FAQ and watched the Reac.js conf video, but I'm still unsure how Relay manages to collect all of the GraphQL queries for a given component tree:

  • Does Relay peeking ahead to see what component a parent renders?
  • Does Relay kind of dry-run the component tree so that it can peek ahead and collection all of the GraphQL statements?
  • Does each component makes it's own query, which gets caught and handled by Relay?
  • Will using Relay/GraphQL require that each parent component explicitly define all of the children components that it renders outside of the render method?
  • Does relay override React.createElement in order to gather and merge all of the GraphQL queries?
@martinandert

On the talk's slides one could see code like this for the FriendInfo component:

return graphql`
  User {
    name,
    mutual_friends { count }
  }
`

What is User in the above code? A root call (without an identifier argument)? A field that specifies some kind of "owner type" for the inner fields? Isn't the type implicated from the context / the surrounding query?

@jeffbski

I am curious like @CoryDanielson about how Relay integrates into the React component life cycle, specifically how does it see what its children need before they are rendered?

I would like to use that approach as well, so I would like to know how you are going about it.

Thanks.

@Kureev
Kureev commented Feb 20, 2015

Hi @wincent, thank you for your extensive gist about Relay and GraphQL.

  1. Will GraphQL do data fetching on the client? I see that server side of GraphQL is only a parser, what about a client side?
  2. Which parts of the GraphQL Facebook is going to release? I assume client-side JS (probably built-in the new React version) + server-side PHP example, right?

Thanks.

@jardakotesovec

Will be possible to somehow also get data from Relay store to flux store and not just to React component?

Use case is when you want to handle some intermediate states before syncing back with server. In this case it would make sense to get data to flux store from Relay, modify it with actions and than again use Relay to update data back on server.

@mziwisky

@jeffbski: GraphQL queries are put on a component's statics property, so they're accessible from the "class" of the component without having to instantiate (or render) it. In the conference talk, when discussing composition, they access these static props with a getQuery class method, e.g. MyComponentClass.getQuery('someQueryName')

@cloudbring

Does the open-sourcing news today at f8 affect this at all? Any news on when Relay, the GraphQL standard and such will be released to the public?

@bogdanbiv

@rmosolgo: The only place where people talk about authentication is https://quip.com/uJQeABv7nkFN, check REACT-ROUTER INCREASES YOUR PRODUCTIVITY. Also relevant for authentication/ authorization: https://groups.google.com/forum/#!topic/reactjs/0jWXxKN1WJ8.

@ccorcos
ccorcos commented Apr 2, 2015

Is there going to be some sort of websocket connection that monitors and updates changes to queries? For example, if I am rendering the results some query, if the values in the database are changed by another user, are those updates immediately reflected in what is rendered?

@devknoll

@martinandert As much as I can tell, it's just for type checking.

@spockwangs

Where does the parsing of GraphQL take place: in the client or the server side? I guess it is implemented in the server side, otherwise the server may respond more fields than what is required by the client.

@nziehn
nziehn commented May 6, 2015

@spockwangs the client as well as the server will parse GraphQL queries: the client will parse them to merge relay queries, the server will parse them the answer the query.

@defvoid
defvoid commented May 7, 2015

Some examples use first() on a connection but how would it be written to get back f.e. only the first and the three last like in the Instragram app feed?

@jonaskello

How is data organized inside the client-side store? Eg. is the data cached in the store by root-call? And how does the store manage to generically do optimistic updates of any data if the root call types are implementation specific?

@pedroteixeira

Anyone knows (or suggests) how Facebook would handle: node(123){ } where node 123 does not exists? Does it return an empty object, null/undefiend or throws exception? Just curious how most people will impl this.

@davbeck
davbeck commented May 18, 2015

Is there any consideration of stale data in Relay? Not as important on web (unless the cache is saved between page loads) but on mobile an app could run continuously for a week or more. How does paging work if the first page is out of date when the second page is requested?

@slorber
slorber commented May 26, 2015

Same as @davbeck: how does cache invalidation works in Relay? Does it follow some kind of eventual consistency model?

@ryantbrown

Is there a rough timeline we can get for when Relay will be available? We're excited about it but can't push it up the chain without some sort of timeframe.

@Savageman

@elierotenberg They released a speakerdeck today mentionning pub/sub live updates, it's on slide 178: https://speakerdeck.com/laneyk/mutations-in-relay?slide=178

@coodoo
coodoo commented May 30, 2015

As @laneykuenzel mentioned on twitter, release date is aimed at later this summer, so I would say end of August maybe? :)

@jlsphar
jlsphar commented Jun 12, 2015

+1 per the @ryantbrown comment above

@cameronroe

In regards to routing, Pete Hunt gave a talk on how he's using webpack async bundles in client-side routing for Instagram.. Thoughts on adopting a similar implementation for Relay? Pete Hunt @ OSCon 2014

@af
af commented Jun 18, 2015

@wincent: A little late to the party, but thanks for all of these details. I have a question about this passage:

There is actually some additional magic going on here — our transform pipeline actually embeds the AST directly (as JavaScript objects) in place of the template — but generally developers won't need to worry about that at all.

Does this mean using the Relay transform pipeline is required, or could this processing optionally be done at run-time? Alternately, could the JS object representation be writable directly, or is it quite verbose? One of the nice things about React is that even if you don't like JSX, you can still define your DOM with plain JS.

@istarkov

It looks like in current react version, one pass server rendering for relay is impossible.

for example somewhere in React we have.

<StoryRelayContainer id={this.props.id} />

StoryRelayContainer doesn't request for a data until rendered.
but on server side we have synchronous interface const html = React.renderToString(component);

And at first render pass we just tell some root containers to get data.
And there can be dependent containers inside StoryRelayContainer, with dependencies on Story object properties, this adds a new render passes also.

So to get rendered html and data on the server we need at minimum two render passes.

@jonira
jonira commented Jul 3, 2015

+1 For a release date. I'm tackling with these same problems and I would prefer not to reinvent the wheel.

@tychotatitscheff

After seeing @vjeux keynote I was wondering if relay was offering a way to do:

  • realtime
  • persistence and offline first

If it don't did you guys thinks that it would be easy to make him interract with something like swarm ?
See gritzko/swarm#59

@sebas5384

Hey @wincent,

Joseph Savona confirmed the release date in his recent session in ReactEurope, so what do you think about updating the answer of the question "When can we have it?" with something like:
Facebook is planning to release Relay on August.

@iofjuupasli

Relay is too complex to be easy.

@leebenson

Release date - closer to Aug 1st, or Aug 31st?

Eager! :-)

@connor11528

Can you use relay and an express server to build iOS apps?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment