Demonstrate Apollo-Server context usage with-apollo-xxx examples
See original GitHub issueFeature request
Current with-apollo- examples do not demonstrate how to use Apollo-Server with context resolver. It doesn’t work out of the box and it’s not at all straightforward for Apollo newbies (like myself) how to enable it.
Note: this is a request for help/consulting for Apollo experts.
1. createIsomorphLink
createIsomorphLink implementations recommended by Apollo Team imply the use of
SchemaLink on server and HttpLink on client. SchemaLink is said to be more performant
because it avoids HTTP layer alltogether:
function createIsomorphLink(ctx) {
if (typeof window == 'undefined') {
// !!! Server side
let {SchemaLink} = require('apollo-link-schema')
let {schema, context} = require('./schema')
return new SchemaLink({schema, context : ctx})
} else {
// !!! Browser side
let {HttpLink} = require('apollo-link-http')
return new HttpLink({
uri: '/api/graphql',
credentials: 'same-origin',
})
}
}
2. SchemaLink
SchemaLink does not initiate an HTTP request. The side effect is that Apollo-Server context function is never called. This function is crucial as it usually fetches or polyfills all common resolver’s data including current user/visitor, etc. Current with-apollo- examples demonstrate how to read user data in local (leaf) resolvers which is not production-like.
Luckily, SchemaLink accepts a context argument where we can provide the same context resolver as to the new ApolloServer({context: ...}).
3. Two calls
Now the tricky part is that createIsomorphLink is called twice. Once with {req, res} context data (coming from getInitialProps) and another – with no context (coming from these:
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
const client = apolloClient || initApolloClient(undefined, apolloState) // !!!
lines).
context function of Apollo-Server must not be called with empty context as it, in most cases, reads ctx.req.
The second invocation of Apollo-Client will reuse cache so resolvers, including context won’t be called. It seems to me that an update like:
function createIsomorphLink(ctx) {
...
- let {schema} = require('./schema')
- return new SchemaLink({schema, context: ctx)
+ let {schema, context} = require('./schema')
+ return new SchemaLink({schema, context : isEmpty(ctx) ? context(ctx) : ctx})
}
should be enough to enable Server’s context usage. Given that apollo/schema.js exports standalone context function of course.
I can make a PR but, before that, it would be great to get a feedback from someone more experienced with Apollo and NextJS.
Are my reasoning correct? Is my proposal valid?
Another option it to always use HttpLink like it’s done in https://github.com/adamsoffer/next-apollo and https://github.com/lfades/next-with-apollo For some reason the authors of both packages decided to not rely on SchemaLink…
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:10 (7 by maintainers)

Top Related StackOverflow Question
Btw I do not agree with the “good-first-issue” tag, this is trickier than it seems and raises questions about state-of-the-art patterns like isomorphic code. There is no clear consensus about how to manage such issues.
Hi, just to give feedback we use
SchemaLinkin Vulcan without much trouble as far as I know. We define different clients for SSR and client-side rendering, literally in different files.Having worked with isomorphic code and SSR for a while, we now tend to consider calls like
ssr: Boolean(ctx)to be anti-pattern, except if you really can’t avoid it or it’s done in a “clean” and limited manner like in Next pages.This is because server-side code tend to diverge slowly from client-side code, and it becomes unmanageable in an app that is multiple years old (unclear bundling process,
require()everywhere). Even if it look alike in the beginning you quickly hit issues like needing different link depending on the environment. The first time you wonder “am I server-side?” in your code is probably the good time to split your file in 2 for client and server. (edit: or most of the time 3 files, one for common code, and 2 smaller files with specific code for each env)An important step is to unify context between REST calls (here the API routes), graphQL calls server side, and graphQL calls client-side. For some reason using a context in queries is not that common in traditional Node app. As far as I understand that’s the point of this issue.