Hi,
I'm using apollo-client to server render a React app. The graphql endpoint is run in the same process as the app that's being rendered, so my networkInterface
has a uri
pointing to localhost.
On my local machine everything is working swimmingly. However on other deployment targets where localhost connections are firewalled (eg Heroku), this doesn't work (the server render docs mention that firewalls can be an issue).
Right now my workaround is to point the uri
to the server's public address, which seems less than ideal from security and performance perspectives. Ideally this app could make server side queries without hitting the network at all, or better yet, by bypassing the http stack entirely.
Is this use case one that the apollo-client could provide an "escape hatch" for? It seems like it'd be a fairly common setup for server-rendered apps and the graphql endpoint to be in the same server process. Here are some possible workarounds, please chime in if you have any other ideas:
This should be pretty simple, just have the server also listen on a domain socket, and point the Apollo client's networkInterface
uri to it. However the whatwg-fetch
http client used by apollo-client does not support these connections AFAICT. I could monkeypatch the global fetch
function to support this (yuck), but better yet, maybe the fetch function could be a parameter to networkInterface
so the caller could customize it as needed? #645 is somewhat related
This would probably be the most performant and secure solution - instead of using a network interface to make graphql queries, pass in a function that can execute them in-process. Here's a really rough sketch of what that might look like:
const client = new ApolloClient({
ssrMode: true,
localInterface: function(graphqlQuery) {
// parse and execute the query here using graphql.js and your own schema
return result : Promise<GraphQLResult>
}
}
Any other suggested approaches for this, or how feasible the two given options are?
@af this is a really interesting issue!
One of the ideas we had early on was for the networkInterface to be entirely pluggable. Essentially, as long as it matches the networkInterface API (i.e. query => promise => GraphQLResult), it can be used!
So, I think this is totally possible without any core modifications and potentially could be a package you could distribute on npm.
// what to match
export interface NetworkInterface {
query(request: Request): Promise<GraphQLResult>;
}
const networkInterface = {
query(request) {
// parse and execute the query here using graphql.js and your own schema
return result : Promise<GraphQLResult>
}
};
const client = new ApolloClient({
ssrMode: true,
networkInterface,
});
I'd be happy to give some advice / guidance if you end up building this route!
@jbaxleyiii Awesome, so my suggestion 2 was already implemented, I just didn't realize it 馃槃 I will take a crack at this in the next couple of days and ping you if I need any pointers. Will re-open the issue if I hit anything that looks insurmountable. Thank you for the quick and helpful response!
@jbaxleyiii So this ended up being much easier than I expected. I haven't fully tested it with all different kinds of queries but this implementation is working great for my app:
const createLocalInterface = (graphql, schema, {rootValue = null, context = null} = {}) => {
const {execute} = graphql
return {
query: ({query, variables, operationName, debugName}) => {
return execute(schema, query, rootValue, context, variables, operationName)
}
}
}
module.exports = {createLocalInterface}
Since the implementation is so small, I'm wondering if a PR would be considered for adding this to apollo-client
(I can rewrite to typescript and submit it). I'm happy to just publish to npm otherwise, but if this is deemed a common enough usecase it would be nice to have it as part of apollo.
Whether or not this gets integrated into apollo-client, here's a really simple npm module that does it: apollo-local-query
@af The link to your repo might be a good addition to the docs about server-side rendering!
@af If I spotted this earlier, I don't need to write createApolloClient.server.js
Most helpful comment
Whether or not this gets integrated into apollo-client, here's a really simple npm module that does it: apollo-local-query