sikanhe / gqtx

Code-first Typescript GraphQL Server without codegen or metaprogramming
458 stars 13 forks source link

Allow interface to contain implementation #60

Open tonyxiao opened 2 years ago

tonyxiao commented 2 years ago

Here's an example of how we we gqtx today to be able to avoid duplicating code between implementers of the ILedger interface.

const LedgerInterface = t.interfaceType<Raw.Ledger>({
  name: 'ILedger',
  fields: () => [
    t.abstractField({name: 'id', type: t.String}),
    t.abstractField({name: 'name', type: t.String}),
    t.abstractField({name: 'users', type: t.List(LedgerUserType)}),
  ],
})

/**
 * Way to share implemention of the LedgerInterface
 * Need to figure out how to share the `users` implementation between books and tabs
 * Or perhaps better to not differentiate at all?
 */
function makeLedgerSubtype(options: {
  name: string
  isTypeOf: (l: Raw.Ledger) => boolean
  fields: () => Array<Field<GQLContext, Raw.Ledger, unknown, {}>>
}) {
  return t.objectType<Raw.Ledger>({
    name: options.name,
    interfaces: [LedgerInterface],
    isTypeOf: options.isTypeOf,
    fields: () => [
      t.field({name: 'id', type: t.String}),
      t.field({name: 'name', type: StringType}),
      t.field({
        name: 'users',
        type: t.List(UserType),
//        resolve: (ledger, __, ctx) =>
      }),
      ...options.fields(),
    ],
  })
}

It's a bit cumbersome, but still better than duplicating the code. In an ideal world the LedgerInterface would just be able to contain the implementation details (optionally) and avoid needing to duplicate all together.

sikanhe commented 2 years ago

I think makeLedgerSubtype is a perfect way of using code level abstractions for shared implementations.

Relay for example use similar helper functions to create types.