mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 01:51:40 -05:00
pass context to the schemaLink too in order to setup resolvers
This commit is contained in:
parent
8a1e1b7a64
commit
ec0e7b9e7f
5 changed files with 65 additions and 56 deletions
|
@ -32,24 +32,27 @@ import { enableSSR } from '../apollo-ssr'
|
|||
*
|
||||
* Config: a config specific to Vulcan
|
||||
*/
|
||||
const createApolloServer = ({ options: givenOptions = {}, config: givenConfig = {}, contextFromReq }) => {
|
||||
const createApolloServer = ({ options: givenOptions = {}, config: givenConfig = {}, customContextFromReq }) => {
|
||||
const graphiqlOptions = { ...defaultConfig.graphiqlOptions, ...givenConfig.graphiqlOptions };
|
||||
const config = { ...defaultConfig, ...givenConfig };
|
||||
config.graphiqlOptions = graphiqlOptions;
|
||||
|
||||
// get the options and merge in defaults
|
||||
const options = { ...defaultOptions, ...givenOptions };
|
||||
const context = initContext(options.context);
|
||||
const initialContext = initContext(options.context);
|
||||
|
||||
// this replace the previous syntax graphqlExpress(async req => { ... })
|
||||
// this function takes the context, which contains the current request,
|
||||
// and setup the options accordingly ({req}) => { ...; return options }
|
||||
const contextFromReq = computeContextFromReq(initialContext, customContextFromReq)
|
||||
// given options contains the schema
|
||||
const apolloServer = new ApolloServer({
|
||||
engine: engineConfig,
|
||||
// graphql playground (replacement to graphiql), available on the app path
|
||||
playground: getPlaygroundConfig(config),
|
||||
...options,
|
||||
// this replace the previous syntax graphqlExpress(async req => { ... })
|
||||
// this function takes the context, which contains the current request,
|
||||
// and setup the options accordingly ({req}) => { ...; return options }
|
||||
context: computeContextFromReq(context, contextFromReq)
|
||||
// context optionbject or a function of the current request (+ maybe some other params)
|
||||
context: ({req}) => contextFromReq(req)
|
||||
});
|
||||
|
||||
// default function does nothing
|
||||
|
@ -90,7 +93,7 @@ const createApolloServer = ({ options: givenOptions = {}, config: givenConfig =
|
|||
|
||||
|
||||
// ssr
|
||||
enableSSR()
|
||||
enableSSR({ computeContext: contextFromReq})
|
||||
|
||||
/*
|
||||
* Alternative syntax with Express instead of Connect
|
||||
|
|
|
@ -45,24 +45,24 @@ export const initContext = currentContext => {
|
|||
return context;
|
||||
};
|
||||
|
||||
// Call on every request
|
||||
export const computeContextFromReq = (currentContext, contextFromReq) => {
|
||||
// @see https://www.apollographql.com/docs/react/recipes/meteor#Server
|
||||
const setupAuthToken = async (context, req) => {
|
||||
const user = await getUser(req.headers.authorization);
|
||||
if (user) {
|
||||
context.userId = user._id;
|
||||
context.currentUser = user;
|
||||
// identify user to any server-side analytics providers
|
||||
runCallbacks('events.identify', user);
|
||||
} else {
|
||||
context.userId = undefined;
|
||||
context.currentUser = undefined;
|
||||
}
|
||||
};
|
||||
// Returns a function called on every request to compute context
|
||||
export const computeContextFromReq = (currentContext, customContextFromReq) => {
|
||||
// givenOptions can be either a function of the request or an object
|
||||
const getBaseContext = req =>
|
||||
contextFromReq ? { ...currentContext, ...contextFromReq(req) } : { ...currentContext };
|
||||
// @see https://www.apollographql.com/docs/react/recipes/meteor#Server
|
||||
const setupAuthToken = async (context, req) => {
|
||||
const user = await getUser(req.headers.authorization);
|
||||
if (user) {
|
||||
context.userId = user._id;
|
||||
context.currentUser = user;
|
||||
// identify user to any server-side analytics providers
|
||||
runCallbacks('events.identify', user);
|
||||
} else {
|
||||
context.userId = undefined;
|
||||
context.currentUser = undefined;
|
||||
}
|
||||
};
|
||||
customContextFromReq ? { ...currentContext, ...customContextFromReq(req) } : { ...currentContext };
|
||||
// Previous implementation
|
||||
// Now meteor/apollo already provide this
|
||||
// Get the token from the header
|
||||
|
@ -91,7 +91,7 @@ export const computeContextFromReq = (currentContext, contextFromReq) => {
|
|||
//}
|
||||
|
||||
// create options given the current request
|
||||
const handleReq = async ({ req }) => {
|
||||
const handleReq = async (req) => {
|
||||
let context;
|
||||
let user = null;
|
||||
|
||||
|
|
|
@ -18,14 +18,16 @@ import { ApolloLink } from 'apollo-link';
|
|||
// import { createHttpLink } from 'apollo-link-http';
|
||||
// import fetch from 'node-fetch'
|
||||
|
||||
export const createClient = (req) => {
|
||||
export const createClient = async ({req, computeContext}) => {
|
||||
// init
|
||||
// stateLink will init the client internal state
|
||||
const cache = new InMemoryCache()
|
||||
const stateLink = createStateLink({ cache });
|
||||
// schemaLink will fetch data directly based on the executable schema
|
||||
const schema = GraphQLSchema.getExecutableSchema()
|
||||
const schemaLink = new SchemaLink({ schema })
|
||||
// this is the resolver context
|
||||
const context = await computeContext(req)
|
||||
const schemaLink = new SchemaLink({ schema, context })
|
||||
const client = new ApolloClient({
|
||||
ssrMode: true,
|
||||
link: ApolloLink.from([stateLink, schemaLink, errorLink]),
|
||||
|
|
|
@ -11,17 +11,17 @@ import {
|
|||
// excepts it is tailored to handle Meteor server side rendering
|
||||
import { onPageLoad } from 'meteor/server-render'
|
||||
|
||||
import renderPage from './renderPage'
|
||||
import makePageRenderer from './renderPage'
|
||||
|
||||
|
||||
const enableSSR = () => {
|
||||
const enableSSR = ({computeContext}) => {
|
||||
Meteor.startup(() => {
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
initializeFragments();
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
// render the page
|
||||
onPageLoad(renderPage)
|
||||
onPageLoad(makePageRenderer({computeContext}))
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -14,40 +14,44 @@ import Head from './components/Head'
|
|||
import ApolloState from './components/ApolloState'
|
||||
import AppGenerator from './components/AppGenerator';
|
||||
|
||||
const renderPage = async sink => {
|
||||
const req = sink.request
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = createClient(req);
|
||||
const makePageRenderer = ({ computeContext }) => {
|
||||
// onPageLoad callback
|
||||
const renderPage = async sink => {
|
||||
const req = sink.request
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = await createClient({req, computeContext});
|
||||
|
||||
// TODO? do we need this?
|
||||
const context = {};
|
||||
// TODO? do we need this?
|
||||
const context = {};
|
||||
|
||||
// equivalent to calling getDataFromTree and then renderToStringWithData
|
||||
const content = await renderToStringWithData(
|
||||
<AppGenerator req={req} client={client} context={context} />
|
||||
)
|
||||
// equivalent to calling getDataFromTree and then renderToStringWithData
|
||||
const content = await renderToStringWithData(
|
||||
<AppGenerator req={req} client={client} context={context} />
|
||||
)
|
||||
|
||||
// TODO: there should be a cleaner way to set this wrapper
|
||||
// id must always match the client side start.jsx file
|
||||
const wrappedContent = `<div id="react-app">${content}</div>`
|
||||
sink.appendToBody(wrappedContent)
|
||||
// TODO: this sounds cleaner but where do we add the <div id="react-app"> ?
|
||||
//sink.renderIntoElementById('react-app', content)
|
||||
// TODO: there should be a cleaner way to set this wrapper
|
||||
// id must always match the client side start.jsx file
|
||||
const wrappedContent = `<div id="react-app">${content}</div>`
|
||||
sink.appendToBody(wrappedContent)
|
||||
// TODO: this sounds cleaner but where do we add the <div id="react-app"> ?
|
||||
//sink.renderIntoElementById('react-app', content)
|
||||
|
||||
// add headers using helmet
|
||||
const head = ReactDOM.renderToString(<Head />)
|
||||
sink.appendToHead(head)
|
||||
// add headers using helmet
|
||||
const head = ReactDOM.renderToString(<Head />)
|
||||
sink.appendToHead(head)
|
||||
|
||||
// add Apollo state, the client will then parse the string
|
||||
const initialState = client.extract();
|
||||
const serializedApolloState = ReactDOM.renderToString(
|
||||
<ApolloState initialState={initialState} />
|
||||
)
|
||||
sink.appendToBody(serializedApolloState)
|
||||
// add Apollo state, the client will then parse the string
|
||||
const initialState = client.extract();
|
||||
const serializedApolloState = ReactDOM.renderToString(
|
||||
<ApolloState initialState={initialState} />
|
||||
)
|
||||
sink.appendToBody(serializedApolloState)
|
||||
}
|
||||
return renderPage
|
||||
}
|
||||
|
||||
export default renderPage
|
||||
export default makePageRenderer
|
||||
// FIRST TRY WITH EXPRESS
|
||||
// However this won't work as Meteor is in charge of loading relevant JS script
|
||||
// This code would only render dead HTML
|
||||
|
|
Loading…
Add table
Reference in a new issue