Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server-side rendering with graphql endpoint on the same server #962

Closed
af opened this issue Nov 25, 2016 · 6 comments
Closed

Server-side rendering with graphql endpoint on the same server #962

af opened this issue Nov 25, 2016 · 6 comments

Comments

@af
Copy link

af commented Nov 25, 2016

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:

1) Connect to the graphql endpoint via a Unix domain socket

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

2) Support server-side queries without using a network interface

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?

@jbaxleyiii
Copy link
Contributor

jbaxleyiii commented Nov 25, 2016

@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!

@af
Copy link
Author

af commented Nov 25, 2016

@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!

@af af closed this as completed Nov 25, 2016
@af
Copy link
Author

af commented Nov 28, 2016

@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.

@af
Copy link
Author

af commented Nov 29, 2016

Whether or not this gets integrated into apollo-client, here's a really simple npm module that does it: apollo-local-query

@helfer
Copy link
Contributor

helfer commented Dec 13, 2016

@af The link to your repo might be a good addition to the docs about server-side rendering!

af added a commit to af/react-docs that referenced this issue Dec 15, 2016
@langpavel
Copy link

@af If I spotted this earlier, I don't need to write createApolloClient.server.js

jbaxleyiii pushed a commit that referenced this issue Oct 18, 2017
@helfer suggested adding a section to the docs in [this issue](#962 (comment))
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants