Skip to content

Handle separate schemas? #87

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

Closed
howesteve opened this issue May 16, 2017 · 22 comments
Closed

Handle separate schemas? #87

howesteve opened this issue May 16, 2017 · 22 comments

Comments

@howesteve
Copy link

Hello,

Is there a (good) way to handle several separate schemas (modularization)? As it is, only a single schema can be parsed by this lib. I have several schemas split in several files.

Thanks,
Howe

@neelance
Copy link
Collaborator

Simple string concatenation is your friend. Resolvers can also live in multiple packages without any issues. Also see #1.

@howesteve
Copy link
Author

Are you seriously proposing concatenation? That's precisely what I meant to exclude when I meant "good" :). How would you deal with multiple type Query {} / schema constructs? They must be removed, and that requires some sort of recompilation.That's why there are multiple tools ([1], [2], [3]) for fixing this.

I understand there can be multiple resolvers, but how to serve them all from the same http endpoint ? I think it would be even better if we could serve multiple resolvers from a single call, thus avoiding schema recompilation and preserving error lines from the original .graphqls files. But currently there seems not to be such an option, or at least I'm not finding it.

Thanks.

[1] https://github.com/okgrow/merge-graphql-schemas
[2] https://github.com/liamcurry/gql/tree/master/packages/gql-merge
[3] https://gist.github.com/icebob/553c1f9f1a9478d828bcb7a08d06790a

@neelance
Copy link
Collaborator

Am I correct that you want to have multiple toplevel fields at the Query type which get contributed by different packages?

You should still be able to create that type via string concatenation quite easily. And for the resolver for Query I would suggest Go's embedded fields, since this will make the methods of all embedded types available on the toplevel resolver type.

@howesteve
Copy link
Author

Actually, each schema file will export their query fields/mutation on their own. That means every subschema will have its own Query{}, Mutation{} etc. and it will be a problem when they can merged all in the same string. Please check this example from [1]:

ClientType

type Client {
  id: ID!
  name: String
  age: Int
  products: [Product]
}

type Query {
  clients: [Client]
  client(id: ID!): Client
}

ProductType

type Product {
  id: ID!
  description: String
  price: Int
}

type Query {
  products: [Product]
  product(id: ID!): Product
}

Merged Result

type Client {
  id: ID!
  name: String
  age: Int
  products: [Product]
}

type Product {
  id: ID!
  description: String
  price: Int
}

type Query {
  clients: [Client]
  client(id: ID!): Client
  products: [Product]
  product(id: ID!): Product
}

I'm not sure if I understood how to serve several schemas from a single endpoint, i.e.:

http.Handle("/query", &relay.Handler{Schema: schema})

relay.Handler() only accepts a single Schema. Do you mean I should write a new handler function (in the lines of relay.Handler()) that loops through all sub schemas, call Schema.Exec() until one succeeds ?

Thanks.

[1] https://github.com/okgrow/merge-graphql-schemas

@tonyghita
Copy link
Member

tonyghita commented May 17, 2017

It sounds like you probably need to write something to merge all of your schemas together. You could probably achieve this by parsing the schemas one-by-one and building a map for them using the introspection package.

You would then use the merged schema to power your GraphQL server.

I'm curious why you want to take this approach.

@howesteve
Copy link
Author

That sounds like a better method. I'll try and see what I get. One advantage is, since the schemas have already been parsed, their errors will be processed before the joining process. That will preserve possible errors line numbers.

I'm using this for modularization, of course. I'm surprised this seems so surprising.

For instance, I have an User schema that is used on several projects. A Person schema, used in several projects as well... there is no point in rewriting all these modules for every project. So for every subproject using them, they will share the same base schemas. But when serving the schema, I must have a single schema to serve, derived from all those subschemas.

Thanks.

@tonyghita
Copy link
Member

tonyghita commented May 17, 2017

I'm surprised this seems so surprising.

I would assume most people have some sort of API gateway layer where the entire schema is defined if they are using a service-oriented architecture.

How do you manage conflicts in the different schemas?

@howesteve
Copy link
Author

Yes, even using that architecture, doesn't change the problem. Sometimes you just have to split your schemas in multiple files. It's a known problem that has arise many solutions from the javascript world, including the apollo guys. I don't think I need to explain more then already has been done.

I tried using the introspection module, but it will complain right away if a type from another module is missing. I think the internal parser is more suited to this.

@neelance , what do you think about implementing this?

Thanks.

@neelance
Copy link
Collaborator

Like I said, a simple solution already exists. I currently still don't see why I should make the API more complicated. Modularization and composition is possible and other users of this library are already doing it.

@howesteve
Copy link
Author

But how a string merge would deal with the example above ? There would be two Query types and the parser would throw an error (of course). Imagine that on 10, 20 schemas. If there is a simple solution for this, please. let me know. I would use the internal parser, but since it's in the internal package, it can't be imported.

About the API change. Two solutions come out:

  1. Implement graphql.ParseSchemas([]string, ...)

  2. Change graphql.ParseSchema(string,... ) to graphql.ParseSchema(interface{},... ), then a type switch handles the string or array of strings. That way no code will be broken and both cases are covered.

Thanks

@neelance
Copy link
Collaborator

Where do your schemas come from? Are they declared as string constants already?

@auwtch
Copy link

auwtch commented May 18, 2017

@howesteve, GraphQL is the graph-based query language it's means that different entities have some relations (bounded-context), if you have several Query types it's means that relations broken. You just lose benefits of GQL.

For different Query types you can use something like this:

schema1 := graphql.MustParseSchema(schemaString1, resolver1)
schema2 := graphql.MustParseSchema(schemaString2, resolver2)
schema3 := graphql.MustParseSchema(schemaString3, resolver3)

And then use different http endpoints for your schemas.

@howesteve
Copy link
Author

@neelance, of course, I have a bunch of .graphqls files defined. They have their queries or mutations defined, and they form a repository of schemas whose I use in multiple projects. So depending on the service I can use the Persons schema, the Users schema but not Products. And so on. But my final schema for each project must be a combination of all these, with all Queries and Mutations merged, forming a single file that is equivalent as all of them were declared together. Please see the js world solutions above.

@DenisNeustroev, I have to disagree. That's why .graphqls files exist. There is no point in having multiple endpoints. Such an horrible thing to do.

@neelance
Copy link
Collaborator

of course, I have a bunch of .graphqls files defined.

This contradicts itself. You either have the ground truth in string constants or in separate files.

A single GraphQL server has a single schema, no way around that. This is the schema that gets served to the client when doing introspection. How you end up with that schema is entirely up to you. If you want to compose it from multiple schemas, then do so, but it is nothing graphql-go need to bother about. Just feed in your final merged schema and you should be fine.

@howesteve
Copy link
Author

OMG ok, thank you, no point in arguing more. You can close this.

@neelance
Copy link
Collaborator

You are being quite arrogant and according to the activity on your GitHub profile you haven't provided anything to the open source world to justify that arrogance at all. We don't just simply do what you say because you say so. Honestly I don't mind at all that you're not going to use my library, there are enough other people in the world who are grateful for work that I offer for free. Feel free to do a fork if you like.

@howesteve
Copy link
Author

That was not the intention, I just couldn't make myself understood. Again, its a known problem with graphql that have arise solutions from other authors.

I apologize if meant rude anyhow.

Thanks.

@neelance
Copy link
Collaborator

I acknowledge the problem, I'm just saying that the solution can be separate from the graphql-go library, there is no need to modify graphql-go to support the solution. The schema language is a string, which can be generated or composed. The resolver is a Go type with methods, which can also be composed for example via embedded fields. All pieces are already available, you just need to put them together for your particular use case. If that solution then is reusable, then offering it as a separate open source project would be helpful to others struggling with the same problem.

@howesteve
Copy link
Author

Ok, I thought I was not clear.

In case this helps someone, the solution is, I'm using gql-merge from https://github.com/liamcurry/gql to merge all schemas, and using go's struct composition to mount all modules resolvers as a single one.

Thanks.

@neelance
Copy link
Collaborator

Cool. Happy to see that you found a solution.

@mununki
Copy link

mununki commented May 13, 2019

@howesteve Any further outcomes to find a tool built in Go to merge modulized *.graphql files?

@mununki
Copy link

mununki commented May 16, 2019

@howesteve Actually, I just made a tool to merge and stitch modularized schema files into one schema. If you're still looking for, take a look.

https://github.com/mattdamon108/gqlmerge

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

No branches or pull requests

5 participants