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

Implement server-side rendering support #261

Merged
merged 11 commits into from
Jun 6, 2019
Merged

Conversation

kitten
Copy link
Member

@kitten kitten commented Jun 5, 2019

Fix #218

Server-side rendering can now be achieved; an app using urql can do this with just minor modifications. Instead of using getDataFromTree like react-apollo does we're using our more generic react-ssr-prepass library.

This still needs to be documented but essentially on the server the client used like so:

import ssrPrepass from 'react-ssr-prepass';

import {
  createClient,
  dedupExchange,
  cacheExchange,
  ssrExchange,
  fetchExchange
} from 'urql';

// We create an ssrExchange, which is an additional cache for client-side rehydration
const ssr = ssrExchange();

// The client can also be shared, but `suspense` needs to be set to
// false on the client-side
const client = createClient({
  url,
  exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange],
  suspense: true
});

// The user has their app somewhere
const App = () => {/* ... */};
// We execute the SSR prepass
await ssrPrepass(<App />);

// Now we can extract the data:
const data = ssr.extractData();

// ...
// This is where renderToString or something goes
// We then need to send down the urql ssr data in the HTML as well
const html = `<script>window.DATA = (${JSON.stringify(data)});</script>`

And on the client we also use the SSR exchange to rehydrate the data:

import {
  createClient,
  dedupExchange,
  cacheExchange,
  ssrExchange,
  fetchExchange
} from 'urql';

// We create an ssrExchange, which is an additional cache for client-side rehydration
const ssr = ssrExchange({ initialState: window.DATA });

// The above can also be used as:
// ssr.restoreData(window.DATA);

const client = createClient({
  url,
  exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange],
  suspense: false // NOTE this is false on the client
});

// ...
// ReactDOM.rehydrate or otherwise

This accepts a Wonka source and throws a promise
if the result is not resolving immediately. This
is done for suspensen and will also limit the
source to a single result.

If the source pushes a value synchronously it
is let through and won't throw a promise, which
also will subsequently let through more values
from the source.
This can be used on the client and the server;
On the server it can start caching results,
while on the client it retrieves cached results
from a rehydrated cache.
@kitten
Copy link
Member Author

kitten commented Jun 5, 2019

I'm currently investigating a remaining issue where during client-side rehydration a race condition leads to a request being sent unnecessarily due to a race condition (potentially?)

Edit: nevermind, it's fixed

This was a whoopsie..
The order of cachedOps$ and forwardedOps$ in ssrExchange
is now reversed. It has been completely swapped to indicate
that the order is important, but only the reverse merge
matters.

Essentially on the client we would delete the result from
the cache in cachedOps$ but then check for whether it was
cached again in forwardedOps$. This would unfortunately
duplicate the operation.
@kitten
Copy link
Member Author

kitten commented Jun 6, 2019

Will potentially be retroactively reviewed again

@kitten kitten merged commit 02c1f07 into master Jun 6, 2019
@helloguille
Copy link

Works nicely. I'll keep testing

@kitten kitten deleted the feat/suspense-promises branch June 13, 2019 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Server side rendering
4 participants