StellateHQ / fuse

Fuse: The fastest way to build and query great APIs with TypeScript

Home Page:https://fusedata.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Centralize `load(ids) => Node` on the Node

mxstbr opened this issue · comments

I don't know if this is possible, but defining .load on every t.loadable is seemingly unnecessary duplication since the node itself could define how to load itself.

I.e. instead of

builder.objectField(LaunchNode, 'rocket', (t) =>
  t.loadable({
    type: RocketNode,
    resolve: (parent) => {
      return parent.rocket.rocket_id
    },
    load: async (ids: string[]) => {
      return Promise.all(ids.map((id) => rocketsDatasources.getOne(id)))
    },
  }),
)

It would be

const RocketNode = node('Rocket', rocketsDatasources).implement({
  isTypeOf: () => true,
  fields: (t) => ({
    cost: t.exposeInt('cost_per_launch'),
    country: t.exposeString('country'),
    company: t.exposeString('company'),
    description: t.exposeString('description'),
  }),
  // This data loader applies to all t.loadable definitions with RocketNode
  load: async (ids: string[]) => {
      return Promise.all(ids.map((id) => rocketsDatasources.getOne(id)))
    },
  }),
})

builder.objectField(LaunchNode, 'rocket', (t) =>
  t.loadable({
    type: RocketNode,
    resolve: (parent) => {
      return parent.rocket.rocket_id
    },
  }),
)

Then, the question becomes: do we still need the distinction between t.field and t.loadable or could we just have t.field? That would further simplify the API surface area to:

builder.objectField(LaunchNode, 'rocket', (t) =>
  t.field({
    type: RocketNode,
    resolve: (parent) => {
      return parent.rocket.rocket_id
    },
  }),
)

This indeed works, I don't know why it did not work before

Amazing! @JoviDeCroock does this work outside of a datasource with a manual .load definition too?

If you explicitly declare load it won't work so it's needed on a t.field, i.e. you need tor resolve the id and it will go back to the node

So this wouldn't work? (without a datasource)

const RocketNode = node('Rocket').implement({
  isTypeOf: () => true,
  fields: (t) => ({
    cost: t.int(),
    country: t.string(),
    company: t.string(),
    description: t.string(),
  }),
  // This data loader applies to all t.loadable definitions with RocketNode
  load: async (ids: string[]) => {
      return Promise.all(ids.map((id) => getRocket(id)))
    },
  }),
})

builder.objectField(LaunchNode, 'rocket', (t) =>
  t.loadable({
    type: RocketNode,
    resolve: (parent) => {
      return parent.rocket.rocket_id
    },
  }),
)

I mean, it would but I don't see the point of it tbh 😅 so we would allow consumers to optionally override load and not pass in a datasource?

Aligned: talking past each other 😆

CleanShot 2023-11-28 at 08 06 35@2x

Every node needs to define a datasource, whether a pre-built one or a custom one.