gabotechs / graphqxl

GraphQXL is a new language built on top of the GraphQL syntax that extends the original language with some additional features useful for creating scalable and big server side schemas. This repository contains the source code for the GraphQXL compiler.
https://gabotechs.github.io/graphqxl
MIT License
272 stars 8 forks source link

Does interface implementing interface support copying resolvers created by directives? #50

Closed taras closed 1 year ago

taras commented 1 year ago

We're creating a GraphQL Plugin for Backstage.io. This plugin provides an schema directives that create resolvers for types and fields. We had to work around the limitation of GraphQL spec by creating fairly complicated schema mapper using 3 schema directives.

The main directive involved in handling "interface implementing interface" is @inherit directive. It copies fields and their resolvers from implemented interfaces. It will generate a type without I prefix for each interface. The I-prefix is ugly and for each type there are 2 interfaces. Ideally, we wouldn't need to create a type this way.

interface Node {
  id: ID!
}

interface IEntity @inherits(interface: "Node") {
  name: String @field(at: 'metadata.name')
  namespace: String @field(at: 'metadata.namespace')
  description: String @field(at: 'metadata.description')
}

interface IComponent @inherits(interface: "IEntity", when: "kind", is: "Component") {
  owner: IUser @relation(name: "ownedBy")
}

Is it possible to copy implementation of directives using GraphQXL? It would be very nice to not have to use I prefix.

taras commented 1 year ago

It looks like it might work. I tried it here https://graphqxl-explorer.vercel.app/

interface Node {
    id: ID!
}

interface Entity implements Node {
    ...Node
    name: String @field(at: "metadata.name")
    namespace: String @field(at: "metadata.namespace")
}

type Component implements Entity & Node @resolve(when: "kind", is: "Component") {
    ...Entity
    owner: User @relation(name: "ownedBy")
}

type User implements Entity & Node @resolve(when: "kind", is: "User") {
    ...Entity
    parent: User @relation(name: "parent")
}

Generates

interface Node {
  id: ID!
}

interface Entity implements Node {
  id: ID!
  name: String @field(at: "metadata.name")
  namespace: String @field(at: "metadata.namespace")
}

type Component implements Entity & Node @resolve(when: "kind", is: "Component") {
  id: ID!
  name: String @field(at: "metadata.name")
  namespace: String @field(at: "metadata.namespace")
  owner: User @relation(name: "ownedBy")
}

type User implements Entity & Node @resolve(when: "kind", is: "User") {
  id: ID!
  name: String @field(at: "metadata.name")
  namespace: String @field(at: "metadata.namespace")
  parent: User @relation(name: "parent")
}
gabotechs commented 1 year ago

directives are intended to work exactly the same as in plain GraphQL, and they will also be inherited if the spread operator is used.

Another behaviour that you might want to take into account is the inheritance of block level directives across type or input assignations:

type Component @resolve(when: "kind", is: "Component") {
    foo: String!
}

type User = Component

compiles to:

type Component @resolve(when: "kind", is: "Component") {
  foo: String!
}

type User @resolve(when: "kind", is: "Component") {
  foo: String!
}

here is a link if you want to play with it.

The example in your second comment looks correct, I hope that can be useful.