Skip to content

Use the types from the superclass #5383

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tim-vandecasteele
Copy link

@tim-vandecasteele tim-vandecasteele commented Jun 13, 2025

So if it's not needed you don't have to define the query type again.

First of all, I have no clue if what I'm doing here makes sense or not, but let me explain what I was after:

When creating a schema from a definition, I got into the case where I wanted to add a set of types to an existing schema, but I wanted to use the GraphQL IDL to do so (see below).

graphql_string = %Q{
  type AdditionalType {
    test: String!
  }
}

GraphQL::Schema::BuildFromDefinition.from_document(OriginalSchema, graphql_string)

Originally I thought that I could do that by passing the OriginalSchema.types to base types, but that's not how these are passed.

I also wanted to see if I can pass these types through extra_types, but I don't know how I get the types in a proper form from the graphql_string.

So if it's not needed you don't have to define the query type again.
@rmosolgo
Copy link
Owner

rmosolgo commented Jun 13, 2025

Wow, this is an interesting idea ... basically .dup a schema but add extra types, is that right?

Depending on your use case, you could do it by going to and from a String, for example:

orig_schema_string = OriginalSchema.to_definition 
extra_schema_types = <<<~GRAPHQL 
type AdditionalType {
  # ... 
 }
GRAPHQL
 
 new_schema_string = orig_schema_string + extra_schema_types 

NewSchema = GraphQL::Schema.from_definition(new_schema_string)

That would work for schema structure, but if you need Ruby-land data (like resolve methods), it won't work.

Could you tell me something about your use case? I bet we can add support for it if it isn't already possible.

@tim-vandecasteele
Copy link
Author

Exactly, dup but adding extra types is the perfect summary :D

Fiddling around I got to something like you proposed but I indeed got to something like the structure, but not so much the ruby functionality.

My usecase is a bit exotic, but let me explain 😄:

We use GraphQL internally for document creation. So you use GraphQL to query the data, which is later on exposed to a template. These templates are made by content experts, they can easily edit the GraphQL queries and the template files, and never have to touch ruby.

Sometimes though, these templates require data that's not available in our models. It's data that is specific to that document template. What I would want to do is allow these content experts to define an addition to the schema, which would automatically resolve to some kind of JSON storage in the back. With the huge advantage that everything is GraphQL typed etc. We would use introspection on the schema extension to build some kind of form, where the data can be entered, and then the GraphQL query can fetch the data based on that specific scheme. (so the resolver would be something like a simple JSON/hash resolver, but could also refer others).

Because I don't want templates to collide with their schema's and types, I want to make separate schema's for the different templates that extend the schema, hence this request 😄.

I hope this makes a bit of sense. Thanks at least for entertaining this idea! Happy to implement it if you give me some pointers what you would consider the proper way to do it.

@tim-vandecasteele
Copy link
Author

Let me know if you have any guidance on a PR you would be willing to accept (because clearly my proposed change makes tests fail :D)

@rmosolgo
Copy link
Owner

Yes, this definitely makes sense. Out of curiousity, would you be creating new GraphQL::Schema subclasses on a request-by-request basis, or would there be several different subclasses created during application boot (but then static during runtime)? I ask because I'm not really sure of the memory implications of making new subclasses during web requests. It'd be interesting to look into...

Another question: how would AdditionalType in the example above be reachable to clients? If it's a free-floating type like that (ie, not a return type of a field, a member of a union, or an implementer of an interface), then there would be no way to actually use it in a schema. I realize this is probably because the example above is contrived -- but how would it work IRL?

Another approach that could be made to work would be just copying over query, mutation, and orphan_types from the "parent" schema. Those entry points lead to all the other types in the schema, so attaching those should result in all other types being copied to the child schema.

As far as final result goes, I think the best API would be to add something like GraphQL::Schema.extend_from_definition(definition), where definition is a String containing GraphQL SDL, and instead of creating a new schema, this method parses the definition and defines new types, fields, etc, based on it. The GraphQL SDL has extend syntax, but I don't think GraphQL-Ruby has any real support for it (yet) beyond parsing (

).

Let me know how you see these new types being integrated with your schema -- maybe providing real support for extend ... would do the job here. If you had that, you could:

new_schema = Class.new(OriginalSchema) do 
  query(OriginalSchema.query)
  mutation(OriginalSchema.mutation)
  orphan_types(OriginalSchema.orphan_types)
end 
new_schema.extend_from_definition(extra_types_string)

@tim-vandecasteele
Copy link
Author

I was thinking of making those on the fly, but in my current configuration I could also create the classes on startup. I need the schema in two moments:

  1. form creation (this would be in a web process, synchronously)
  2. during document generation (this runs in a background job)

AdditonalType would be reachable only by the concept that also defines that schema extension. I have it setup at the moment (with the current code in this PR) where there's a document.graphql and a document.schema.graphql. The first one being the actual query, the other one being extension definition.

GraphQL::Schema.extend_from_definition(definition)
This seems like a great API, I'll have a shot at that.

FYI, currently (with the original change that copies the types types = schema_superclass.types.dup), this seemed to more or less do the job (still in the implementation phase):

  def self.schema_with_addition(additional_schema_path)
    GraphQL::Schema::BuildFromDefinition.from_document(self, GraphQL.parse_file(additional_schema_path), default_resolve: nil)
  end

So it looks like the Builder::build method seems to do exactly what I want, except from inheriting the types.
What's the reason the types (or a subset of) should not be copied when doing the BuildFromDefinition? (and would it be ok to extend that method to accept types?)

With respect to extend, that does the job for my usecase. I have a Params type that is defined statically, and then in my schema offset, I have:

extend type Params {
  start_date: ISO8601Date!
  signing_date: ISO8601Date!
}

Executing my current code (as defined above), exposes the proper fields and in my statically defined Params class, I can catch the fields with method_missing.

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

Successfully merging this pull request may close these issues.

2 participants