node.js - Hvordan implementere isTypeOf-metoden?


Gitt dette skjemaet:

interface INode {
  id: ID
}

type Todo implements INode {
  id: ID
  title: String!
}

type Query {
  node(id: ID!): INode
}

Gitt denne klassen:

export default class Todo {
  constructor (public id: string, public title: string) { }

  isTypeOf(value: any): Boolean {
    return value instanceof Todo;
  }
}

Gitt denne resolveren:

type NodeArgs = {
  id: string
}
export const resolver = {
  node: ({ id }: NodeArgs) => {
    return new Todo('1', 'Todo 1');
  }
}

Når jeg kaller spørringen:

query {
  node(id: "1") {
    id
    ... on Todo {
      title
    }
  }
}

Da får jeg returverdien nedenfor:

{
  "errors": [
    {
      "message": "Abstract type INode must resolve to an Object type at runtime for field Query.node with value { id: \"1\", title: \"Todo 1\" }, received \"undefined\". Either the INode type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "node"
      ]
    }
  ],
  "data": {
    "node": null
  }
}

Som du kan se, har jeg implementert isTypeOf-funksjonen, men jeg får fortsatt feilmeldingen.

Hva gjør jeg galt?

Notater :

  • Jeg bruker Typescript, express og express-graphql;

Svar #1

isTypeOf er en funksjon som blir sendt til konstruktøren av en GraphQLObjectType når du oppretter skjemaet ditt programmatiske. Det samme gjelder for resolveType-funksjoner og unions/interfaces. Hvis du bruker SDL og oppretter skjemaet ditt ved hjelp av buildSchema, er det ingen måte å injisere disse funksjonene i det opprettede skjemaet, akkurat som du ikke har en måte å gi resolvers for felt på typer annet enn Query og Mutation.

Du har et par alternativer. Ett alternativ er å bruke standard resolveType-adferd. Dette sjekker etter en __typename-egenskap på objektet, og faller tilbake til å kalle isTypeOf på hver implementerende type til det matcher. Det betyr at hvis du bruker en klasse, bør det være tilstrekkelig å gjøre noe slikt:

export default class Todo {
  get __typename() {
    return 'Todo'
  }
}

Den beste løsningen ville være å droppe buildSchema og bruke makeExecutableSchema fra graphql-tools. Deretter kan du definere dine resolveType- og/eller isTypeOf-funksjoner direkte i dine resolvere. For eksempel:

const resolvers = {
  Query: {
    node: (obj, args, context, info) => {
      return new Todo('1', 'Todo 1')
    }
  },
  INode: {
    __resolveType: (obj, context, info) => {
      if (obj instanceof Todo) return 'Todo'
    },
  }
}

Ikke bare kan du enkelt definere isTypeOf eller resolveType på denne måten, du kan også enkelt legge til resolvere for felter av enhver type og legge til tilpassede skalare uten problemer. Du kan ikke gjøre noe av dette (enkelt) hvis du bare bruker buildSchema.

Rediger :

Hvis du foretrekker å bruke isTypeOf i stedet for resolveType, vil resolverne se omtrent slik ut:

const resolvers = {
  Query: {
    node: (obj, args, context, info) => {
      return new Todo('1', 'Todo 1')
    }
  },
  Todo: {
    __isTypeOf: (obj, context, info) => {
      return obj instanceof Todo
    },
  }
}

Bare én eller den andre er nødvendig. Enten skriver du en resolveType-funksjon for hver abstrakte type du bruker, eller skriver du en isTypeOf for hver objekttype som kan være en abstrakt type.

Relaterte artikler