Closed SachaG closed 5 years ago
Related:
Also we will now use the official Meteor/Apollo integration:
Here's a round-up of the relevant code that will need to be migrated/refactored/discarded:
vulcan:lib
clientauth.js
: https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/client/auth.js probably not needed anymoreinject_data.js
: https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/client/inject_data.js reconsider if still neededrender_context.js
: https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/client/render_context.js port to Apollo Client 2 if still neededvulcan:lib
modulesapollo.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/modules/apollo.js port to Apollo 2 (see client/apollo2.js
)redux.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/modules/redux.js replace with apollo-link-statevulcan:lib
serverapollo_server.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/server/apollo_server.js port to Apollo Server 2inject_data.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/server/inject_data.js reconsider if neededmeteor_patch.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/server/meteor_patch.js reconsider if neededrender_context.js
https://github.com/VulcanJS/Vulcan/blob/apollo2/packages/vulcan-lib/lib/server/render_context.js migrate to Apollo Server 2vulcan:routing
See contents of https://github.com/VulcanJS/Vulcan/tree/devel/packages/vulcan-routing/lib Currently moved to vulcan:core/client/start.jsx
.
Here is a summary of my latest commits. Globally, beware of bugs. The current branch should work fine, but I only tested it on the example-forms
of the Starter package for the moment.
/voyager
endpoint thanks to @ApollinaireBut... there are a few things to handle, and I am not knowledgeable enough to fix them yet:
user
and userId
in context. Now we use meteor/apollo
getUser
, which encapsulate the logic that was need in v1 (checking the token and so on). I noticed that sometimes the authorization
header is "null"
(as a String, not the null
object`, because a JSON was stringified) though, this is not very clean, maybe we can improve the client part.Authorization
header when using GraphQL PlayGround with localStorage['Meteor.loginToken
]. See https://github.com/prisma/graphql-playground/issues/510 for global Headers (right now we have to create an initial tab to setup headers) setup. The best solution seems to be using cookies. Also, in GraphiQL, we used the passHeader
option that allowed access to the localStorage, but it was awfully hacky. See https://github.com/prisma/graphql-playground/issues/849
Note: Apollo 2 Chrome plugin provides a GraphiQL tab out of the box.
Edit: maybe using cookies would be the solution?Other things to do:
context.YourCollection
) for now is a good idea. We can introduce Data Sources in a next version, while keeping the current syntax as a legacy for a while, there is no emergency. Also beware, the update might be trickier as it seems as there is caching involved.Notes from my learning:
apollo-sever-express
, though we don't need to explicitely create an Express app (meteor/webapp
does it for us). There are currently no example of a Meteor integration with a standalone Apollo server (so without express, using only the builtin HTTP server), and I am not sure it is actually doable.RR4 works but I did not take a look at the second point (adding callbacks, options). See https://github.com/VulcanJS/Vulcan/issues/2096 for example
withMessages
hoc is a State Link usage exampleI did a test with using only an Express middleware, but this can only render dead HTML, as scripts are never injected by Meteor.
So I ended up using meteor/server-render
onPageLoad
callback, which behave mostly the same as an Express middleware. SSR is simply adding relevant HTML to the body of the response generated by Meteor. Then Apollo and React will handle the rehydratation.
[X] create an Apollo client for the server (it fetches all the data it can during SSR). It relies on SchemaLink
for the moment. Thus the server does not need to request himself using HTTP, it directly calls resolvers.
[X] render the App as HTML server side.
[X] populate the document
using Helmet[X] inject the Apollo state
[X] return the rendered HTML on requests
Previously we relied on dynamicHead
, that is then used by the template-web.browser.js
file of _boilerplate-generator
. Do we still need this? Instead I use meteor/server-render
features to append content to the request body.
This first set of features is enough to render a basic page like the getting-started
first steps. Following features allow more complex usage, based on the example-forms
app.
[X] handle Apollo Link State server side. The stateLink API is now available client side and server side.
[X] Define context of the SchemaLink, which is in turn passed to the resolvers and is necessary to actually get data.
This context is then used in 2 places, as the context
option of the server (using for each query), and as the context
option of the SchemaLink (used during the initial SSR render). It is recomputed for every request.
[X] Cookies. Could be improved though, as universal-cookies
middleware does not seem to be taken in to account (see issue with meteor/server-render
and meteor/webapp
below.
[X] Add auth with Cookie. On first page load, the Authorization header can't be set, so you can't authenticate. Solution is to rely on a cookie instead, and then only on the Authorization header when it is set. The cookie is updated on client startup based on the localStorage token. I uncommented existing code in auth.js
and updated it, it seems fine.
[ ] Fix #2111 (Form SSR/withSingle HOC is broken)
[ ] integrate advanced features of vulcan:routing
(options, callbacks, etc.)? Some of them are now unnecessary, to be tested
[ ] handle errors. For the moment if the render fails we only display an error client side. In production you'd want the error to be caught and fallback to client side rendering, with maybe a console.warn (to be discussed). In dev mode you'd like the error to print nicely. Try for example to trigger an error in the default_resolvers, it will be hard to debug. I've added the ErrorLink server side to Apollo client but I don't know what it should do.
[ ] Check server side cache. Try the example-forms
, fill a new Customer but do not add an address: you'll get crappy error messages about the cache (cache.readData
is not defined) + error handling is broken. Sometimes the error is correct, sometimes it's weird.
If you add an address it will correctly save the data however.
[ ] check for broken features, performance regression etc.
[ ] write. tests. many tests.
Less relevant stuffs:
meteor/server-render
onPageLoad
callback. The middleware runs correctly, but the sink.request
object is still the initial request. See https://github.com/meteor/meteor-feature-requests/issues/174#issuecomment-441047495 for more details (example with Cookies)Note: for the moment you should be able to disable SSR by commenting the line of apollo-server
that triggers the enableSSR()
function.
We will have to study thoroughly existing code in vulcan:routing
(thanks @xavczen for the walkthrough!), as it handles many edge cases and provides options.
vulcan-redux
package. It allows legacy app to work by simply loading this package and imporing addAction
, addReducer
etc. directly from there. It allowed me to test the router.xxx.wrapper
callbacks.router.server.wrapper
: allow to wrap the App component, for example to add a Redux store. Added to AppGenerator.jsx
(untested though).router.client.wrapper
: same but client siderouter.server.postRender
(used to injectJSS for example): now takes the sink
object + context
as param instead of req/res. vulcan-material-ui
will need a very small update to handle this breaking change, see https://docs.meteor.com/packages/server-render.htmlvulcan:styled-components
independent package router.server.preRender
, router.server.html
can be safely ignored, at least for the moment. They are not used in major packages/open source Vulcan example. wrapper
and postRender
already provides enough flexibility to add a complex lib such as Styled Components or Redux, including SSR.vulcan:core/modules/callbacks
: need to reset flash messages on route changeUsing Meteor forces us to keep using apollo-sever-express, though we don't need to explicitely create an app. There are currently no example of a Meteor integration with a standalone Apollo server (so without express, using only the builtin HTTP server).
Maybe someone from the Meteor/Apollo team could help us? Or maybe @lorensr would know?
Don't know! I had this question as well: https://github.com/apollographql/apollo-client/pull/3739#issue-203997257
@benjamn Is there a better way to set up Apollo Server inside Meteor? We're currently recommending https://www.apollographql.com/docs/react/recipes/meteor.html#Server
Hi,
Actually I think I figured this out wrong in the first place. It's not apollo-server-express
that spawns the Express instance, it's just a connector. You always have to pass your own Express instance.
meteor/webapp
does however setup a full fledged server (with connect to be precise, I guess this is mostly the same thing as Express? Not familiar with Node subtleties), with cookies, compression, etc., and your custom handlers. Code is here
So apolloServer.applyMiddleware({ app: Meteor.connectHandlers })
is roughly equivalent to apolloServer.applyMiddleware({app: yourExpressServer })
. And thus you have to use apollo-server-express
, since under the hood you are using Express when using meteor/webapp
.
So my question itself does not make sense, you can't use the Apollo builtin server, at least not using the meteor/webapp
package, because it's implementation rely on Express already. Anyway in Vulcan case it's a better idea to rely on Express due to 3rd party plugins compatibility (see graphql-voyager integration tested by @Apollinaire).
Edit: To sum it up:
meteor/webapp
.const app = express(); app.use(...); WebApp.connextHandlers.use(app)
, you are actually chaining a new custom Express server to the WebApp Connect server. In most case this is redundant.WebApp.connectHandlers.use(yourMiddeware)
(eg for graphql-voyager etc.) or WebApp.connectHandlers.use(config.path, yourMiddleware)
(eg for cookies, optimization etc.) instead of creating a new Express server.Closing in favor to https://github.com/VulcanJS/Vulcan/issues/2171
I'm opening a new thread to track my progress on the migration to Apollo 2.0 (starter repo).
Main Tasks