VulcanJS / Vulcan

πŸŒ‹ A toolkit to quickly build apps with React, GraphQL & Meteor
http://vulcanjs.org
MIT License
7.98k stars 1.89k forks source link

Apollo SSR #1486

Closed SachaG closed 7 years ago

SachaG commented 7 years ago

This is a starter issue, highly inspired by the great job of the folks at Hoodie. πŸ‘

πŸŽƒπŸ™€πŸ‘•πŸ”­πŸ˜» Hacktoberfest: Trick or Treat!

If you haven’t yet, sign up for Hacktoberfest to earn an exclusive T-Shirt.

We are sure you can learn a cool trick or two in the process on how to hack & customize Telescope Nova! πŸ”­

πŸ€” What you will need to know

Meteor, React, Apollo, and GraphQL.

More info:

Figure out how to integrate Apollo with server-side rendering.

πŸ“‹ Step by Step

Working on your first Pull Request? You can learn how from this free series How to Contribute to an Open Source Project on GitHub.


Ping us here, in the Telescope Slack room or on Twitter if you have any questions πŸ˜‰

I'll leave @xavcz complete this issue with more details :)

xavxyz commented 7 years ago

We may need to use react-router-redux to bind the Redux state with the router state, as explained here https://github.com/thereactivestack/meteor-react-router-ssr/issues/89#issuecomment-252849287 and in the example provided by the meteor package.

We'll dive into this soon, any help appreciated!

xavxyz commented 7 years ago

I can't get the fetchData (react-apollo) function to work with the fetchDataHook (reactrouter:react-router-ssr).

I'll try with the implementation of https://github.com/apollostack/GitHunt-React/blob/master/ui/server.js

xavxyz commented 7 years ago

So it's working by tweaking reactrouter:react-router-ssr code:

I don't like the fact we have to declare let variables undefined at first, such as initialState, store, client in the routes, and then let them be overwritten by the hooks of ReactRouterSSR.

This will need a refactor to use Nova without ReactRouterSSR and newest APIs of react-apollo.

Note: thanks @jasonnathan (https://github.com/thereactivestack/meteor-react-router-ssr/issues/101) for the inspiration πŸ‘

jasonnathan commented 7 years ago

Actually @xavcz, I hacked server.jsx too to get it working in a way that seemed more apollo / react-router-ssr friendly.

I moved preRender down after wrapperHook and gave it the {app} argument.

On a side note, wrapperHook, being a client option is ambiguous considering it was called on the server too.

After that, my setup looks like this:

let client, initialState,
     url = "localhost",
     opts = {
       ssrMode: Meteor.isServer
     };

// relevant for apollo when a client-side query is made
if(Meteor.isClient && process.env.NODE_ENV === 'production'){
  url = "public-url.com";
}

// setup apollo's networkInterface
opts.networkInterface = createNetworkInterface({
  credentials: 'same-origin', uri: `http://${url}:3000/graphql`
});

// I think this is what you were referring to where its undefined till hook is called
const rehydrateHook = state => initialState = state;

const wrapperHook = app => {
  opts.initialState = initialState;
  client = new ApolloClient(opts);
  return <ApolloProvider client={client}>{app}</ApolloProvider>
};

// the preRender is simpler. All it needed was the `app` argument
const preRender = (req, res, app) => Promise.await(getDataFromTree(app));

// dehydrating in a way that is apollo friendly. Queries & mutations need to be removed
const dehydrateHook = () => ({apollo:{data:client.store.getState().apollo.data}})

const htmlHook = html => {
  const head = ReactHelmet.rewind();
  return html.replace('<head>', '<head>' + head.title + head.base + head.meta + head.link + head.script);
}

// the weirdest thing, to put wrapperHook in clientOptions, inferring it only runs on the client
const clientOptions = {wrapperHook, rehydrateHook};
const serverOptions = {htmlHook, preRender, dehydrateHook};

ReactRouterSSR.Run(AppRoutes(), clientOptions, serverOptions);

I wouldn't give up on react-router-ssr so quickly though. It does do quite a number of things that would otherwise require a whole more grunt work. Like moving scripts to the <body /> and injecting Meteor's generated CSS.

I'm saying this because I started on my own version and ended up doing many things the same way... :)

xavxyz commented 7 years ago

Hehe yes, I followed the same path, actually I walked through the code of your site to understand. It's pretty smart πŸ‘ Then I've hacked in some other places RR:SSR to adapt it the way we setup routes & apollo client in Nova.

On the next steps, we'll take inspiration on https://github.com/apollostack/react-apollo/blob/master/examples/meteor/server/index.js & & GitHunt, and try what happens without RR:SSR (and so FastRender... and InjectData local patch)

jasonnathan commented 7 years ago

Cool link, thanks!