Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minimal Relay example #106

Closed
pyrossh opened this issue Oct 27, 2016 · 39 comments
Closed

Minimal Relay example #106

pyrossh opened this issue Oct 27, 2016 · 39 comments

Comments

@pyrossh
Copy link

pyrossh commented Oct 27, 2016

How can I use relay to populate my data? Will it work automatically? or any plans on how to add support for it?

@ugiacoman
Copy link

Any success?

@pyrossh
Copy link
Author

pyrossh commented Oct 29, 2016

I have yet to try it... mostly today?

@daytonna
Copy link

Im new too
On Oct 29, 2016 3:39 AM, "pyros2097" notifications@github.com wrote:

I have yet to try it... mostly today?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#106 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AWD9FxgmSSDYtTRZzNyBzDjQBkKTiUXzks5q4vhOgaJpZM4Kh991
.

@sedubois
Copy link
Contributor

sedubois commented Oct 29, 2016

Or in general, how to add a GraphQL client?

E.g Apollo (which unlike Relay 1 is fully GraphQL compliant)? 0.5.0 just came out.

@thorstenweber83
Copy link

thorstenweber83 commented Oct 29, 2016

I created a higher order component to wrap routes that take data from apollo:

import React from 'react'
import 'isomorphic-fetch'
import ApolloClient, { createNetworkInterface } from 'apollo-client'
import { getDataFromTree } from "react-apollo/server"
import { ApolloProvider } from 'react-apollo'
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';

const getReducer = ( client ) => {
  return combineReducers({
    apollo: client.reducer( ),
    someReducer: ( state = {}, action = null ) => action && action.state
      ? action.state
      : state
  });
}

const getClient = ( headers ) => {
  return new ApolloClient({
    networkInterface: createNetworkInterface({ uri: 'http://mygraphqlserver.local/graphql' }),
    ssrMode: true,
    headers: headers
  })
}

export const initClientAndStore = ( initialState, isServer, headers ) => {
  if ( isServer && typeof window === 'undefined' ) {
    const client = getClient( headers )
    return {
      client,
      store: createStore(getReducer( client ), initialState, applyMiddleware(client.middleware( )))
    }
  } else {
    if ( !window.clientAndStore ) {
      const client = getClient( headers )
      window.clientAndStore = {
        client,
        store: createStore(getReducer( client ), initialState, compose( applyMiddleware(client.middleware( )), window.devToolsExtension
          ? window.devToolsExtension( )
          : f => f ))
      }
    }
    return window.clientAndStore
  }
}

const getRootComponent = ( {
  client,
  store
}, Component ) => (
  <ApolloProvider client={client} store={store}>
    <Component prop={'test'}/>
  </ApolloProvider>
)

export default( ComposedComponent ) => class WrapWithApollo extends React.Component {
  static async getInitialProps({ req }) {
    const isServer = !!req
    const headers = req
      ? req.headers
      : {}
    const clientAndStore = initClientAndStore( {}, isServer, headers );

    await getDataFromTree(getRootComponent( clientAndStore, ComposedComponent ))

    return { initialState: clientAndStore.store.getState( ), isServer, headers }
  }

  constructor( props ) {
    super( props )
    const clientAndStore = initClientAndStore( props.initialState, props.isServer, props.headers );
    this.clientAndStore = clientAndStore;
  }

  render( ) {
    return getRootComponent( this.clientAndStore, ComposedComponent )
  }
}

it mostly works, but im getting a strange warning about the markup checksum:

next-dev.bundle.js:8985 Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) <div data-css-1mbax
(server) <div data-css-1mbax

@jbaxleyiii
Copy link

@thorstenweber83 that looks like a react Apollo SSR bug we are tracking and hope to have fixed soon! This looks pretty great. I'd love to create a react-apollo-nextjs package. Would you be up for that?

@zth
Copy link

zth commented Oct 30, 2016

A general package for Apollo (with SSR) + next.js would be awesome indeed, I've been sketching a little myself on something similar but didn't get very far yet. I'd be glad to just help out testing if you guys need any assistance.

@sedubois
Copy link
Contributor

sedubois commented Oct 30, 2016

@thorstenweber83 I haven't figured out how I can configure the endpoint URI through an environment variable. Client-side, it seems the constructor calls initClientAndStore before it gets that info from the server?

@sedubois
Copy link
Contributor

I took @thorstenweber83's code and wrapped to take care of the graphql higher-order component as well and also display loading and error states (but still no idea how to pass the endpoint URI as environment variable):

https://github.com/sedubois/realate/blob/master/containers/Apollo.js

Apologies if there are any mistakes, but at least it works so far. Would be great if newcomers didn't have to figure out all of that by themselves.
/cc @stubailo

@stubailo
Copy link

It's really surprising to me that it's this hard to set up a global container, like Redux, with this framework. Is there an official answer for how to use Redux with Next.js?

@stubailo
Copy link

Ah, I found the wiki page, I guess it suggests something similar.

Perhaps someone can maintain a package like next-apollo or similar to do this whole singleton integration thing?

@sedubois
Copy link
Contributor

sedubois commented Nov 1, 2016

@stubailo I think that's what @jbaxleyiii had in mind, I think it would be great too 😊

Maybe things could also be further simplified with #88.

@sedubois
Copy link
Contributor

sedubois commented Nov 3, 2016

In the first example I gave above, the data is only rendered client-side. But how to render Apollo server-side?

@thorstenweber83, above you render server-side with <Component prop={'test'} />, not sure what you intended by that?

@stubailo In your GitHunt example, you do SSR using react-router which passes the props for rendering, but I don't know how to do it without react-router. Help appreciated 😊

@sedubois
Copy link
Contributor

sedubois commented Nov 3, 2016

OK I'm starting to understand this getDataFromTree(), we first need to inject the component rendered with dummy data so that Apollo knows which data to query to populate the store.

So I did that but then got server-side Network error: fetch is not defined at apollo-client/transport/networkInterface.js:73:16. Shouldn't Apollo make sure to have what it needs to make HTTP requests server-side? I added the 'isomorphic-fetch' as @thorstenweber83 did above.

@sedubois
Copy link
Contributor

sedubois commented Nov 3, 2016

Related issue: apollographql/apollo-client#645

@sedubois
Copy link
Contributor

sedubois commented Nov 3, 2016

OK as for lacking SSR, my problem came from having wrapped in a higher-order component and not re-calling getInitialProps (as described here). Sorry for the spam 😄

@adamsoffer
Copy link
Contributor

Has anyone been able to put together a working example of next.js and apollo-client?

@sedubois
Copy link
Contributor

sedubois commented Nov 29, 2016

Yes @ads1018, here! https://github.com/relatecommunity/relate I'll get back to work on it in a few days, but already has Next and Apollo.

@adamsoffer
Copy link
Contributor

adamsoffer commented Nov 29, 2016

Sweet! Thanks @sedubois. I wonder if there's a way to pass the Apollo store to the root component without having to wrap all pages in a HOC. Perhaps that will be possible when Next releases a Programmatic API (discussed in #291)

@sedubois
Copy link
Contributor

@ads1018 actually new things could already be possible thanks to the merge of #301 but haven't looked into that yet (and that code isn't released yet).

@arunoda
Copy link
Contributor

arunoda commented Dec 2, 2016

Guys, Next.js had a pretty big issue with #253.
Basically we couldn't share JS modules between next.js pages.

With now v1.2.x we fixed it. So Apollo, Redux, Relay and any other data binding tool works without by default without any additional work.

Anyway, we should have some example showcasing that.

@adamsoffer
Copy link
Contributor

adamsoffer commented Dec 2, 2016

@arunoda amazing news! I've been itching to use next.js for my next project but have been holding off until it plays nice with apollo-client. Looking forward to checking out the example.

@dzcpy
Copy link

dzcpy commented Dec 3, 2016

Same here, I really look forward to seeing some examples with apollo-client.

@nickredmark
Copy link

I posted an example with apollo in #387 - thought I'd create a new issue since it's not about relay.

@chompomonim
Copy link

I thought this thread is about Relay support! For Apollo there are a few other issues opened (#99, #387), so maybe all apollo related discussions should be moved there?

@timneutkens timneutkens changed the title Relay Support? Minimal Relay example Jan 16, 2017
@thisbejim
Copy link

thisbejim commented Jan 19, 2017

One problem, when using isomorphic-relay, is that prepareInitialRender is async, so the component immediately mounts on the client without enough info to render properly. As suggested in this issue you can get around this by faking initialReadyState, e.g:

const initialReadyState = {
  aborted: false,
  done: true,
  error: null,
  events: [
    {
      type: "STORE_FOUND_ALL",
    }
  ],
  ready: true,
  stale: false,
};

And passing it into IsomorphicRelay.Renderer:

export default class extends React.Component {
  static async getInitialProps () {
    if (isServer) {
      // fetch data and set initial props
      const { data, props } = await IsomorphicRelay.prepareData(rootContainerProps, networkLayer);
      return { serverProps: props, data };
    }
  }

  constructor(props) {
    super(props)
    if (isServer) {
      // initial server-side render
      this.state = { example: props.serverProps };
    } else if (props.data) {
      // inject data on client after server render
      IsomorphicRelay.injectPreparedData(environment, props.data);
      // pass in fake initialReadyState, skipping prepareInitialRender
      this.state = { example: { ...rootContainerProps, environment, initialReadyState } };
    } else {
      // client only render
      this.state = { example: { ...rootContainerProps, environment } }
    }
  }

  render () {
    return (
      <IsomorphicRelay.Renderer {...this.state.example} />
    );
  }
}

But, as the author of isomorphic-relay said in that issue, this isn’t really ideal.

Another thing to keep in mind is that if you are thinking of using a hosted graphql service like Graphcool it might not be worth going with relay server rendering anyway. You’re looking at ~1 second waiting for a response from the service and the subsequent initial server render.

@Gregoor
Copy link

Gregoor commented Jan 25, 2017

@thisbejim I agree, I only went with Graphcool because it was easy to setup and wouldn't litter the repo with graphql schema related files and dependencies.

I'd like to finish the next.js example, though I don't know how to properly initialize the Relay enviornment, if someone has an idea or interest in it, here's the repo:
https://github.com/Gregoor/next.js-relay-example

And my current issue:
denvned/isomorphic-relay#61 (comment)

@helfer
Copy link

helfer commented Feb 3, 2017

FWIW @ads1018 found a pretty neat way to get Apollo working with almost no boilerplate in #387. See the example in the wiki.

@Gregoor
Copy link

Gregoor commented Feb 15, 2017

I wish Relay's API was isomorphic out of the box, but that just doesn't seem to be a priority for FB atm.

@timneutkens
Copy link
Member

I wonder if we should keep this open or not. Maybe we should just document the reason why using Apollo over Relay is better in the case of isomorphic rendering.

@Gregoor
Copy link

Gregoor commented Feb 23, 2017

I'd definitely keep this open, imo apollo-client and Relay are too far apart to consider them just a replacements of each other. While apollo might get more primitives right, there are things I really like about Relay they that apollo gets wrong:

  • fetching diffs
  • cursor-based pagination
  • fragments

Maybe I'll find the time to figure out how exactly the Relay API would have to change, to make the integration with next simpler.

@helfer
Copy link

helfer commented Feb 23, 2017

I don't have a strong opinion on whether or not to close this issue (if I had to choose, I would say keep it open), but I just wanted to address the points made by @Gregoor, because I think they aren't valid (to be fair, the fragment one used to be valid). While neither is a drop-in replacement of the other, I think it's worth pointing out that with Apollo you can do everything that you can do with Relay, but not the other way around.

To address the specific points made:

  • Fetching diffs: I think this point is based on Relay advertising query diffing as a feature in the past, when in fact it was a mistake. Apollo intentionally doesn't do query diffing because we recognized the importance of static queries. The Relay team has recognized that query diffing was a mistake, so they explicitly said that when Relay 2 comes out it will have static queries like Apollo does now.

  • Cursor-based pagination: I think this is a fair point, but I think it's important to put it in context: Apollo lets you do any type of pagination. Relay makes pagination with connections easier, but it doesn't work with any other type of pagination. I'll be the first to admit that if you absolutely know that you want to use connections everywhere in your schema for the rest of time, and it's the only thing you care about, Relay is the way to go until Apollo has the same or better user experience for pagination with connections.
    If you're not 100% sure however, consider that a paginated query in Relay has to be at least as complicated as this:

query {
  viewer {
    post(id: "34def7cb-9702-426d-9883-07beaf2d8240") {
      comments {
        edges(first: 10, after: "ba2f4523-93f2-4f3b-9135-073dc465779c"){
          cursor
          node {
            text
          }
        }
      }
    }
  }
}

while in Apollo it can be simply this:

query {
  post(id: 502){
    comments(first: 10, after: 72240){
      text
    }
  }
}

Which one you prefer might be a matter of taste, but I'm willing to bet that most people will find the second query much easier to understand and aesthetically more pleasing. If you want to use the first style of query, you can still do that in Apollo, but you cannot use the second style of query in Relay.

note: I intentionally used long ids in the Relay example, because Relay requires all ids to be unique across your schema.

PS: we're planning to add special support to make cursor-based pagination super-easy in one of the upcoming versions of Apollo.

  • Fragments: I assume that by this you mean co-locating data requirements with the component by using fragments. Apollo lets you do that as well. It's also worth pointing out that in Relay's you need to set up an extra build step with babel and use Relay.QL, while Apollo works out of the box and uses plain old GraphQL.

Now that said, I agree 100% with you that neither should be considered a replacement of the other, and that each client has its own strengths and weaknesses. Personally I think it would be great if Relay was easier to use and integrated better with other libraries,

@Gregoor I would love to know what things you wish Apollo did/had that Relay currently does/has. We're always striving to make Apollo better, so your input would be much appreciated and valued! I also hope that once Relay 2 finally comes out it will be possible to use it with next.js. I'm also hoping that there will be some really innovative features in it that we can adopt in Apollo as well 😀

@Gregoor
Copy link

Gregoor commented Feb 23, 2017

Thanks @helfer, intriguing points!

  • fetching diffs:

The Relay team has recognized that query diffing was a mistake

Dang, that's a bit sad. Related Thread is here btw, for anyone that's interested. I hope it gets reimplemented at some later point.

  • Cursor-based navigation: neat example, but it would be even neater if you'd include fetching further results. For me that's where Relay wins, as it is a simple setVariables call to fetch more whereas the apollo variant looked a bit more obscure (I'd be happy to be proven wrong :) ).

  • fragments: To include a fragment in Relay, you simply embed a getFragment-call of a component in the right place. In Apollo you have to embed it AND spread the right name at the right place. I think that API is at least twice as complex 🙃
    Also I haven't yet figured out how you'd set variables inside of a fragment. Is that possible?

@stubailo
Copy link

In Apollo you have to embed it AND spread the right name at the right place. I think that API is at least twice as complex

I would be surprised if you don't have to do that in Relay 2 because it makes static analysis quite a bit more difficult. Perhaps they require you to use a variable name that's identical to the fragment name or something.

Also I haven't yet figured out how you'd set variables inside of a fragment. Is that possible?

That's also a Relay 1-specific feature, I don't think Relay 2 has it because of the move to static queries.

@Gregoor
Copy link

Gregoor commented Feb 24, 2017

Yaiks, I hope it doesn't turn out that way, then Relay 2 would be quite the downgrade for me. I really do appreciate the isolation of variable setting in Relay and if Relay 2 is more apollo like that would make it a lot more complex wouldn't it? I feel like I'm missing something here.

@sedubois
Copy link
Contributor

I think the general Apollo vs. Relay debate is very interesting but a bit off-topic concerning Next.js 😄

If we agree Relay 1 can't practically be integrated with Next.js, I'd just close the issue. And we can reopen a new one when Relay 2 is a thing. @timneutkens ?

@Gregoor
Copy link

Gregoor commented Feb 24, 2017

We might have gotten a bit sidetracked true 😁

I added another commit to https://github.com/Gregoor/next.js-relay-example which makes it kinda work. The kinda part is, that there is just no proper way to rehydrate Relay, so the first client-side render is empty (and thereby inequal to the server's render) but without refetching the client renders again correctly. Atm I don't see any API in Relay that would allow completely fixing it, so closing this is fine from my side.

@jasonkuhrt
Copy link

Relay 2 is a thing now.

@timneutkens
Copy link
Member

Closing in favor of #1757

@lock lock bot locked as resolved and limited conversation to collaborators May 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests