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

How can Apollo-Server users begin to adopt Hasura? #3812

Closed
rektide opened this issue Jan 31, 2020 · 8 comments
Closed

How can Apollo-Server users begin to adopt Hasura? #3812

rektide opened this issue Jan 31, 2020 · 8 comments

Comments

@rektide
Copy link

rektide commented Jan 31, 2020

Hello. I'm coming in to a company that started using Apollo Server in the relatively recent past, & they've already built a good chunk of code in & on it. I would very very much like to introduce Hasura, as I believe it may help us write & maintain a lot less code, could likely be much faster to run, & it's tooling is incredible.

How can I introduce Hasura to the company? I think I do not want to have multiple GraphQL clients running at once; that sounds like an anti-pattern & asking for trouble. If I'm trying to build a new relatively isolated feature, how can I develop this feature atop Hasura, without re-engineering the rest of the my systems to also use Hasura?

Does anyone have any resources or documentation or blog posts or articles talking to how they began & started a mixed Hasura #graphql environment? I would very much like to have something to go off of if I want to introduce Hasura into this company.

At the moment, the (I think) most helpful resource I've found is in a comment from @0xR, talking about introducing graphql-transform-federation atop Hasura to implement my own Apollo Federation solution:

You can add a middleware service with graphql-transform-federation to add federation decorators to an existing hasura schema. This makes it possible to use hasura behind an apollo federation gateway. Let me know what you think.

My understanding is that this would let me easily introduce Hasura underneath the existing code that we have & need to continue to run to keep production going, while be begin to adopt Hasura in production too. This sounds great in concept. I'm kind of hung up though, in that I don't know what doing that work of adding graphql-transform-federation would look like or how I would do that. I would have to suss out- well, I'm not even sure what I would have to suss out- to begin adopting Hasura under this path.

In sum, I've found what I think is a helpful path to be able to introduce Hasura & begin increasingly adopting it, but this path feels like it could be quite an adventure & I'm not even sure if it is the right path. Please, Hasura, what resources if any are presently available for helping us very many Apollo Server users begin to adopt & enjoy your wonderful & oh so helpful offering? Is graphql-transform-federation a good path? Are there other paths we should try? Is there any documentation or write ups you can point to for how Apollo Server users can achieve Hasura adoption? I'd really like to see Hasura take off & grow & get adopted widely, both within my company, & at large, & I'm hoping this issue can help express what I as an incoming engineer am finding to be the biggest barrier to getting Hasura up & going quickly, efficiently, & in a trustworthy manner, in production.

@GavinRay97
Copy link
Member

The answer here really has less to do with Apollo Server than your data layer.
Is your DB currently Postgres, or at the very least another common SQL DB?

If so, you can just point Hasura at your DB (in case of Postgres), or you can migrate MySQL/MS-SQL Server to Postgres with a single command using pgloader:

https://pgloader.io/about

Now you will have full GraphQL CRUD & Aggregation API for all your tables.

If you are using a non-relational datastore, there isn't much you can do.

For the emphasis on Apollo specifically, I think this may stem from a misunderstanding in the developer community at-large about what exactly GraphQL (and related technologies for it) are.

GraphQL does not exist. It is not a technology, framework, or library you can download & install. GraphQL is a specification -- an idea.

When you use or write a GraphQL server, what you are doing is creating an application who's API architecture follows a set of rules. As with REST, saying an API is "RESTful" does not describe "what" it is, but "how" it is. You can expect certain properties and methods to exist in this API, and for it to behave in certain ways.

The point behind this tirade is that Apollo has much ado about nothing. It is yet another library to provides a spec-compliant GraphQL implementation -- and not even a performant implementation at that. Apollo Server + Express is ~80% slower with ~11.5 times the latency of top-performing Node.js GraphQL servers. See below.

https://github.com/benawad/node-graphql-benchmarks#benchmarks

To say you use "Apollo Server" is to say that you have a GraphQL schema defined + resolvers that say "When asked for this field, do this thing". That's all a GraphQL server is, a field -> resolver mapping that the server uses to map incoming requests to the functions you define as resolving them.

Anyways, my point being is that from a data standpoint you could swap Apollo out for any other spec-compliant server in theory with zero effects (minus whatever vendor-specific tooling you may be using around it).

As far as integrating with Hasura goes:

You can have it automagically generate a full API based on your DB, and then keep Apollo for all of the business logic.

It would be easier to stitch Apollo's schema into Hasura, and expose your graph to the client through there because of Hasura's ability to handle permissions & authorization. But you totally could also stitch Hasura's schema into Apollo, and either expose the Hasura methods to your client or keep them on the server and use them as a replacement for the regular DB calls you would be making in your resolvers. This way is backwards and incredibly inefficient though.

For schema-stitching ("remote schemas"? "federated graphs?" whatever they call them these days), to clear up any lack of understanding, it's similar to an API gateway or middleware/proxy. When you stitch schemas from other GraphQL sources, the primary gateway forwards requests to the respective graph to be resolved when those particular fields are called. This allows you to expose several (micro)services under a unified graph and gateway (endpoint).

Hope some of that is of any use.

@rektide
Copy link
Author

rektide commented Feb 2, 2020

It would be easier to stitch Apollo's schema into Hasura, and expose your graph to the client through there because of Hasura's ability to handle permissions & authorization. But you totally could also stitch Hasura's schema into Apollo, and either expose the Hasura methods to your client or keep them on the server and use them as a replacement for the regular DB calls you would be making in your resolvers. This way is backwards and incredibly inefficient though.

At this point I think the only safe thing for us to do with our production traffic is to start with the latter option, "stitch Hasura's schema into Apollo."

We are OK with the inefficiency, given that we already live with it regularly. Hopefully we can change latter!

For now though, how do we start? Are there any docs or resources that might be suggested to help us begin to adopt Hasura by,

schema-stitching ("remote schemas"? "federated graphs?" whatever they call them these days),

I also don't know what they are calling it. I'm just looking for help & guidance getting started in adopting Hasura, via whatever means short of replacing all our current work.

@tirumaraiselvan
Copy link
Contributor

tirumaraiselvan commented Feb 2, 2020

@rektide Great questions.

First important thing to know is that Hasura and Apollo can indeed work together. You can add Apollo server to Hasura as a Remote Schema or you can add Hasura to Apollo Gateway as a federated schema (using a small trick: link). Previously, you could stitch Hasura to Apollo Server easily as well but, since Apollo Federation, they have started to deprecate that tooling. (EDIT: An example implementation is given in the following comment: #3812 (comment))

Lots of proper nouns above :) But let's break it down according to your current setup.

  1. You have one Apollo Server serving your GraphQL API: This is the easiest case and the best way to adopt Hasura in this situation is to add the Apollo Server into Hasura as a Remote Schema. The final API that is served will be exactly the same as before, but with other Hasura features as well.

  2. You have one Apollo Gateway federating multiple GraphQL servers: Two options here -

    a. Add Hasura as another federated service - There is a small trick that you can employ to server Hasura behind Apollo Gateway and it is mentioned here. Like you mentioned, tools like graphql-transform-federation might also help (but I haven't tried it).

    b. Use Hasura as Gateway: You can move the individual federated Schemas behind Hasura and start defining relationships across all of them via Remote Joins. This is probably not an incremental solution, but it is a solution so putting it out there.

Happy to answer more questions and break it down further according to specific needs or use-cases that you may have.

@coco98
Copy link
Contributor

coco98 commented Feb 2, 2020

@rektide Yep. Great questions and thank you for your kind words reg Hasura ❤️

To summarize what @GavinRay97 and @tirumaraiselvan are saying, the key questions to answer before we go ahead with a plan of action are:

  1. Are you already using Postgres?
  2. What is the current authentication setup like?
  3. Do you already have more than 1 graphql service that you're having to stitch together?

PS: Thanks @GavinRay97 for the helpful notes making things more precise. This will be useful to folks as they come across this thread with similar questions :)

@GavinRay97
Copy link
Member

GavinRay97 commented Feb 2, 2020

You already got answers from the 🧠 Great Wizards 🧙 themselves, if you want a literal implementation of it would look something like this roughly:

// hasuraSchemaBuilder.js
import { setContext } from 'apollo-link-context'
import { HttpLink } from 'apollo-link-http'
import fetch from 'node-fetch'

const hasuraGraphQLEndpoint = 'http://localhost:8080/v1/graphql'
const http = new HttpLink({ uri: hasuraGraphQLEndpoint, fetch })

// Add admin secret header so that your Apollo server has unrestricted access
const link = setContext((request, previousContext) => ({
  headers: {
    'x-hasura-admin-secret': `your admin secret here/read from env`,
  }
})).concat(http)


export default async () => {
  const schema = await introspectSchema(link);

  const executableSchema = makeRemoteExecutableSchema({
    schema,
    link,
  })

  return executableSchema
}
// main.js
import buildHasuraSchema from './buildHasuraSchema'
// Lets pretend this is where you call makeExecutableSchema and export it
import defaultSchema from  './schema'

async function main() {
  const hasuraSchema = await buildHasuraSchema()
  const executableSchema = mergeSchemas({
    schemas: [
      defaultSchema,
      hasuraSchema,
    ]
  })
  const server = new ApolloServer({ schema: executableSchema })
  server.listen().then(({ url }) => {
    console.log(`🚀 Server ready at ${url}`)
  })
}

main()

I have no idea if this runs/works, its off the top of my head but it should be generally correct I think? 🤷‍♂️

@gentleShark
Copy link

gentleShark commented Feb 13, 2020

The code above pointed me in the right direction and helped me put together the code below that works. It uses 2 different HttpLinks: one for handling introspection and the other for forwarding along the JWT Authorization header. Adding here in case it helps:

// hasuraSchemaBuilder.js
import { setContext } from 'apollo-link-context';
import { HttpLink } from 'apollo-link-http';
import { makeRemoteExecutableSchema, introspectSchema } from 'graphql-tools';
import fetch from 'isomorphic-fetch';

const hasuraGraphQLEndpoint = 'http://localhost:8080/v1/graphql';
const httpLink = new HttpLink({ uri: hasuraGraphQLEndpoint, fetch });

// Add admin secret header so that your Apollo server has unrestricted access
const adminSecretLink = setContext(() => ({
	headers: {
		'x-hasura-admin-secret': `your-hasura-admin-secret`,
	},
})).concat(httpLink);

const authLink = setContext((_request, prevContext) => {
    if (prevContext.graphqlContext && prevContext.graphqlContext.headers) {
        return {
            headers: {
                'Authorization': prevContext.graphqlContext.headers.authorization,
            }
        }
    } else {
        return {}
    }
}).concat(httpLink);

export default async () => {
	const schema = await introspectSchema(adminSecretLink);

	const executableSchema = makeRemoteExecutableSchema({
		schema,
		link: authLink,
	});

	return executableSchema;
};

@rektide
Copy link
Author

rektide commented Mar 25, 2020

Good answers all. Thanks for the help. I'm happy letting this get closed? Is that the right path? Thanks thanks thanks, each & every one of ya!

@tirumaraiselvan
Copy link
Contributor

@rektide Thanks, closing this then. We can reopen and continue discussion here if you have further questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants