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

Add ability to implement schema using both SDL and GraphQLObjectTypes #185

Closed
migueloller opened this issue Oct 24, 2016 · 18 comments
Closed
Labels
enhancement feature New addition or enhancement to existing solutions

Comments

@migueloller
Copy link

This is a continuation of my comments on #116.

To answer @stubailo's question:

With graphql-utilities the idea is very simple, GraphQL.js provides a JavaScript implementation to define a GraphQL schema but we could always use the less verbose schema language. The initial goal of the library was to allow anybody to build anything that could be built with GraphQL.js but using the schema language.

For example,

to build a schema:

const { __schema } = build(`
  schema {
    query: Query
  }
`, [Query]); // Query is a GraphQLType whose name is `Query`

to build a type:

const { Query } = build(`
  type Query {
    status: Boolean!
  }
`, {
  status: () => true, // here we use a shortcut for the resolver of the `status` field.
});

here's another type:

const { Weekday } = build(`
  enum Weekday {
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
  }
`);

A few things to note:

  • You can build more than one type in a single call to build, it would simply be exported with its name in the returned type map. When building a single type, you could return that type instead of a type map but I've found this to result in a more confusing API (the library currently does that but I was planning to remove support for it).
  • The __schema name above is a simple selection, it could be any other name but it was the one that made the most sense after much thought.
  • The template strings could be wrapped with graphql-tag for syntax highlighting and other nice perks. Implementing this would be extremely simple.
  • There are various shortcuts implemented to improve dev experience. The most useful one is the resolver shortcut, instead of having to write an object with a resolver field, you simply pass in the resolver. Here's the difference with and without:

with shortcut:

const { Query } = build(`
  type Query {
    status: Boolean!
  }
`, {
  status: () => true,
});

without shortcut:

const { Query } = build(`
  type Query {
    status: Boolean!
  }
`, {
  status: {
    resolver: () => true,
    // here you can add descriptions or deprecation reasons.
  },
});
  • I've approached this library with the mentality that you want to define as much as you can in the schema language and only use the configs for what's absolutely necessary. For example, descriptions have to provided via the schema language using comment blocks. Of course, it could be left up to the developer to do it either in the schema language or in the config file but this could cause confusion as to which description to pick if there are two present for a field (one in the config and one in the description).
  • The library currently does schema inference (like buildASTSchema) but this type of magic makes the API a bit confusing. It could be kept or removed but what I know for sure is that good error messages are essential for debugging these things.
  • Finally, the general signature of build takes the following parameters:
    • The type definition (this is a string, document, source, whatever can define the type using the schema language).
    • A config object. This is the same object that would get passed to the corresponding GraphQL.js constructor in new GraphQLSchema(config), for example. This can change if there are shortcuts.
    • An array or a thunk that resolves to an array of type dependencies. If there are any types that are referenced in the schema language definition then these types should be present here. A thunk has to be supported ti support circular references.

There are more things I have in mind for this library (like custom types for URLs, Email, etc.) but I feel like starting with build is a good start. What graphql-tools currently does with appplying resolvers by modifying the schema after the fact feels hacky, and doesn't lend itself for colocation of resolvers and definitions which is why ultimately I built graphql-utilities.

If my approach is something of interest I would love to discuss it more and potentially even make it a part of graphql-tools as I'm honestly just looking to benefit the ecosystem as a whole and not have another library to compete (https://xkcd.com/927/ heh).

Thanks for all the great work you guys have been doing with the Apollo stack. Truly amazing stuff! 😄

@helfer
Copy link
Contributor

helfer commented Oct 31, 2016

I like that approach, and I think it's complementary to graphql-tools. GraphQL-tools has the advantage that the schema becomes much easier to read, while graphql-utilities has the advantage of keeping the resolver code close to the schema types that it applies to.

@migueloller
Copy link
Author

migueloller commented Oct 31, 2016

@helfer,

You can always separate the resolvers like you do in graphql-tools and then call build(declaration, resolvers, typeDependencies) with all your declarations and resolvers.

Colocating the resolvers is an advantage but it's not 100% required if you don't want to do it that way.

EDIT: You could build the entire schema just like in graphql-tools and simply concatenate all the strings and pass the entire schema declaration into build.

@DxCx
Copy link
Contributor

DxCx commented Jan 25, 2017

@migueloller are you willing to create a PR adding this feature? :)

@migueloller
Copy link
Author

@DxCx, definitely! Could you take a look at migueloller/graphql-utilities#12

There I go over some of the work I want to do to get the build API in a stable place. After you go over that I'd love to discuss what would be the best way this could be integrated with graphql-tools. If this API is implemented the API for graphql-tools would change a little bit (instead of the typeDefinitions argument being an array of strings it would be an array of GraphQL.js types).

I'm in the Apollo Slack as @miguel if you want to have a conversation there! Would be more than happy to chat about the motivation behind the build API.

@DxCx
Copy link
Contributor

DxCx commented Jan 30, 2017

hi @migueloller,
sorry it took me a while to check this =)

anyway, yeah, this is more or less what i had in mind,
the ability to transform typeDef + resolver into a map,
so the user can use this API in two different apis,
it can be either passed to makeExecutableSchema along with the others,
(I think we might want another key in the options for those, like prebuilt or something, else they should go to resolvers)
or buildGraphqlType (or any other name) to make it a graphql object.
that should give people the ability to compose their GQL into modules.

also, we need to think how to specify context requirements for a type..
for example, type X expects context to have getX function.

@migueloller
Copy link
Author

@DxCx,

Awesome! I'm currently working on migueloller/graphql-utilities#12 as fast as I can so I'll let you know as soon as that's done.

With regards to context requirements, do we really want to limit how context gets defined by the library consumer? Shouldn't they simply provide a root value that takes advantage of the default resolver?

@DxCx
Copy link
Contributor

DxCx commented Jan 30, 2017

oh no, i don't want to limit anything,
i'm just saying we need to think how to document it so it can be shipped with the built "module" that's all ;)

@stubailo
Copy link
Contributor

Hey I'm going through and looking at some of the issues here, and this is still something that comes up a lot! We're going to try to address some of these in the coming weeks :)

Going to use this as the main issue for this idea.

@stubailo stubailo changed the title Building intermediary GraphQL types for incremental adoption and code separation Add ability to implement schema using both SDL and GraphQLObjectTypes Jul 13, 2018
@stubailo stubailo added the feature New addition or enhancement to existing solutions label Jul 13, 2018
@migueloller
Copy link
Author

@stubailo, sounds good!

Please let me know if you want any help/contributions.

@PedramMarandi
Copy link

Any update on this issue?
I'm looking for a temporary solution to obtain such a functionality, is there a ny way?

@HosseinAgha
Copy link

@stubailo did you have any progress on this?

@yaacovCR yaacovCR mentioned this issue Mar 27, 2020
22 tasks
@yaacovCR
Copy link
Collaborator

Folding into #1306.

@borekb
Copy link
Contributor

borekb commented Jul 31, 2020

@yaacovCR I took a look at #1306 but didn't quite understand how (or whether) this issue was resolved. As of v6, is it possible to build a schema from parts where some of them use SDL and some of them plain graphql-js?

@yaacovCR
Copy link
Collaborator

This is possible using stitchSchemas using types property?

but that is not really new functionality, it was just given a new property name but it used to work just fine in the schemas property before that was broken out into subschemas...

@borekb
Copy link
Contributor

borekb commented Jul 31, 2020

Aha, I might be confused here, thought it's an issue about mergeTypeDefs / @graphql-tools/merge. We don't use schema stitching, just merging. Do you know if that works? All examples show SDL and I wasn't able to find much info about plain graphql-js support.

@yaacovCR
Copy link
Collaborator

Not as familiar with the merge package. @ardatan ?

Note that you don't actually have to use stitching here, If you don't pass any sub schemas you can just merge types and typedefs using the stitching function but there is no delegation

@yaacovCR
Copy link
Collaborator

Then you would get a schema output, what kind of output are you looking for

@danielrearden
Copy link
Collaborator

@borekb mergeTypeDefs accepts an array of the following types: string | Source | DocumentNode | GraphQLSchema.

The function works by merging types represented as AST nodes, which is what you get when you use graphql-tag or call parse on a string containing SDL. If the function receives a string, Source or GraphQLSchema, it can convert those inputs back into an AST object and merge them that way.

The function doesn't support passing in instances of GraphQLObjectType or the other type classes directly. However, if those types are part of a schema, you can just pass in the schema. If they are not part of any schema, you can still pass them in this way:

const schema = new GraphQLSchema({
  types: [FooType, BarType],
});

const document = mergeTypeDefs([schema]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement feature New addition or enhancement to existing solutions
Projects
None yet
Development

No branches or pull requests

9 participants