Skip to content

Commit

Permalink
Merge pull request #232 from voidale/master
Browse files Browse the repository at this point in the history
ssr with meteor
  • Loading branch information
helfer authored Dec 25, 2016
2 parents 7aab66a + 677f111 commit ce4334e
Showing 1 changed file with 91 additions and 0 deletions.
91 changes: 91 additions & 0 deletions docs/source/meteor.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,94 @@ It will use the same port as your Meteor server. Don't put a route or static ass

You may still use the authentication based on DDP (Meteor's default data layer) and apollo will send the current user's login token to the GraphQL server with each request. But if you want to use only GraphQL in your app you can use [nicolaslopezj:apollo-accounts](https://github.com/nicolaslopezj/meteor-apollo-accounts). This package uses the Meteor Accounts methods in GraphQL, it's compatible with the accounts you have saved in your database and you may use apollo-accounts and Meteor's DDP accounts at the same time.


## SSR
There are two additional configurations that you need to keep in mind when using [React Server Side Rendering](http://dev.apollodata.com/react/server-side-rendering.html) with Meteor.
1. Connect your express server to Meteor's existing server with [WebApp.connectHandlers.use](https://docs.meteor.com/packages/webapp.html)
2. Do not end the connection with `res.send()` and `res.end()` use `req.dynamicBody` and `req.dynamicHead` instead and call `next()`. [more info](https://github.com/meteor/meteor/pull/3860)

The idea is that you need to let Meteor to finally render the html you can just provide it extra `body` and or `head` for the html and Meteor will append it, otherwise CSS/JS and or other merged html content that Meteor serve by default (including your application main .js file) will be missing.

Here is a full working example:
```js
import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
import React from 'react';
import ReactDOM from 'react-dom/server';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { ApolloProvider } from 'react-apollo';
import { renderToStringWithData } from 'react-apollo';
import { match, RouterContext } from 'react-router';
import Express from 'express';
import 'isomorphic-fetch';
import Helmet from 'react-helmet';

import routes from '../both/routes';
import rootReducer from '../../ui/reducers';
import Body from '../both/routes/body';

// 1# do not use new
const app = Express();

app.use((req, res, next) => {
match({ routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', error); // eslint-disable-line no-console
res.status(500);
} else if (renderProps) {
const client = new ApolloClient({
ssrMode: true,
// networkInterface: createLocalInterface(graphql, schema),
networkInterface: createNetworkInterface({
uri: Meteor.absoluteUrl('/graphql'),
opts: {
credentials: 'same-origin',
headers: req.headers,
},
}),
});

const store = createStore(
combineReducers({
...rootReducer,
apollo: client.reducer(),
}),
{}, // initial state
compose(
applyMiddleware(client.middleware())
)
);

const component = (
<ApolloProvider store={store} client={client}>
<RouterContext {...renderProps} />
</ApolloProvider>
);

renderToStringWithData(component).then((content) => {
const initialState = client.store.getState()[client.reduxRootKey].data;
// the body content we want to append
const body = <Body content={content} state={initialState} />;
// #2 `req.dynamicBody` will hold that body and meteor will take care of
// actually appending it to the end result
req.dynamicBody = ReactDOM.renderToStaticMarkup(body);
const head = Helmet.rewind();
// #2 `req.dynamicHead` in this case we use `react-helmet` to add seo tags
req.dynamicHead = ` ${head.title.toString()}
${head.meta.toString()}
${head.link.toString()}
`;
// #2 Important we do not want to return this, we just let meteor handle it
next();
});
} else {
console.log('not found');
}
});
});
// #1 connect your express server with meteor's
WebApp.connectHandlers.use(Meteor.bindEnvironment(app));
```

0 comments on commit ce4334e

Please sign in to comment.