Apollo-link-rest: Feature-Diskussion: Hinzufügen einer automatischen Typnamen-Inferenz

Erstellt am 26. Jan. 2018  ·  22Kommentare  ·  Quelle: apollographql/apollo-link-rest

Hi,

Nach @fbarthos PR (#55) zum Hinzufügen von Typnamen zu verschachtelten Objekten erwartete ich, dass es automatisch Typnamen basierend auf dem Eigenschaftsnamen der Objekte ableiten kann. Dies war jedoch nicht der Fall.

Nachdem er mit ihm über Slack gesprochen hatte, schlug er vor, dass ich ein Problem eröffnen sollte, damit wir diskutieren können, ob dies machbar wäre, da die Eigenschaft selten mit dem TypeName übereinstimmen könnte.

In meinem Anwendungsfall würde dies es ermöglichen, einen GraphQL-Client schnell mit einem REST-Server zu verbinden, während nur die erforderlichen benutzerdefinierten __typenames mit der Implementierung von @fbartho bereitgestellt werden .

Ich habe diese Funktion implementiert und werde in Kürze eine PR öffnen.

enhancement💡 question❔

Hilfreichster Kommentar

@mpgon & @Rsullivan00 Ich würde das Hinzufügen eines Abschnitts #### Related Libraries mit einem Link und einer einzeiligen Beschreibung Ihrer Bibliotheken zu unserer README unterstützen.

Alle 22 Kommentare

Danke für den Beitrag @sky-franciscogoncalves!! Ich denke, das ist wichtig zu diskutieren.

Meine persönliche Sorge war, dass in den GraphQL-APIs, die ich sah, nur die einfachsten von query einen übereinstimmenden Feldnamen hatten, der sicher in eine Pascal-Groß-/Kleinschreibung in ein __typename . Viele von ihnen hatten alternative Konjugationen, während fast alle mutation s einfach nicht funktionierten.

Im Allgemeinen denke ich, dass die Leute bei der Implementierung von typePatcher besorgt waren, "gefährliche" Magie zu haben, die Sie vielleicht nicht wollen - und dann bedingt deaktivieren müssen. => Komplexität!

Ich bin jedoch immer noch ein Neuling bei GraphQL, also würde ich mich freuen , wenn @jbaxleyiii oder @sabativi Feedback zu dieser Feature-Idee

Wenn ich meine eigene graphql-API durchschaue, sehe ich nicht viele Fälle, in denen die Pascal-Groß-/Kleinschreibung des Felds mit dem Typ übereinstimmt, da ich dies normalerweise tun würde

type Action {
  kind: ActionKind!
  name: String!
}

Hinweis, damit Pascal-Großschreibung funktioniert und nicht mit anderen möglichen Typen kollidiert, müsste ich freundlich nennen -> actionKind, was sich komisch anfühlt, da Sie bereits wissen, dass Sie sich in einer Aktion befinden

type Action {
  actionKind: ActionKind!
  name: String!
}

Ich denke, Sie könnten das übergeordnete Schema (Action) mit den verschachtelten Feldern Pascal cased name (Kind) verketten, wodurch Sie ActionKind . Obwohl dies Sharing-Typen verhindern würde, z

type Schedule {
  start: Float
  end: Float
}
type Task {
  schedule: Schedule!
  name: String!
}
type Job {
  schedule: Schedule!
  name: String!
}

Was halten Sie davon, die vorhandene Anmerkung zu überladen, damit sie für verschachtelte Felder ohne das Pfadattribut verwendet werden kann?

query MyQuery {
  action @rest(type: "Action", path: "action/") {
    id
    name
    kind @rest(type: "ActionKind") {
      name
    }
  }
}

Ich weiß, dass dies bedeutet, es jedes Mal hinzuzufügen, wenn Sie dieses Feld in der Abfrage verwenden, anstatt ein einziges Mal während der Initialisierung des Rest-Links. Aber IMHO stört mich ein wenig zusätzliche Ausführlichkeit für die Deutlichkeit. Außerdem definieren Sie bereits den Typ des Schemas der obersten Ebene, sodass es konsistenter erscheint, den Typ der untergeordneten Elemente definieren zu müssen.

Wobei sich das typePatcher-System für mich seltsam anfühlt, da Sie am Ende Typen an zwei sehr unterschiedlichen Stellen definieren und dieses Problem zu weit von der Abfrage wegbewegt wird.

@cloudkite : Wenn Sie sich dieses Ticket #48 ansehen, haben wir die Option einer @-Direktive besprochen. Meine allgemeine Erkenntnis war, dass es meistens fehleranfällig sein würde, wenn Benutzer sie jedes Mal hinzufügen müssen, wenn sie Felder zu ihren Auswahlsätzen hinzufügen und entfernen.

Allerdings könnte eine @type(…) Direktive größtenteils additiv sein, daher würde ich einer Implementierung dieser Strategie nicht allzu sehr ablehnend gegenüberstehen. - meine 2 Cent.

Der Versuch, etwas vom Typ abzuleiten, wird nie in allen Fällen funktionieren, da REST keinen Standard hat und jeder seine eigene Art hat, Endpunkte und Ressourcen zu benennen ... Ich sehe also keine gute Zukunft dafür.
Was ich jedoch interessant finde, ist die @-directive-Option, die wieder auf den Tisch kommt und die einfach einzurichten zu sein scheint, aber es besteht die Möglichkeit, dass die Leute sie manchmal vergessen, also brauchen wir eine Möglichkeit, sie zumindest davor zu warnen.

Als persönliche Lösung arbeite ich mit Schemata für mein apollo-link-state / apollo-link-rest Setup in meinem Unternehmen und ich habe ein Tool geschrieben, das diese Dateien überwacht/parsen und mein typePatcher Code generiert. apollo-codegen auf, um TypeScript-Bindungen zu generieren. Ich würde es hier teilen, aber es ist kein wirklich freundliches Skript, daher denke ich nicht, dass es eine allgemein anwendbare Lösung ist, aber es wird für die Bedürfnisse meines Teams funktionieren.

Vielen Dank für Ihr Feedback. Ich bin noch sehr neu bei GraphQL, daher entschuldige ich meine mangelnden Kenntnisse.

Meiner Meinung nach kann die aktuelle Lösung für Neulinge verwirrend sein. Da es nicht ganz klar ist, wie der TypePatcher funktioniert und wie der Benutzer ihn verwenden soll.

Ich mag die Lösung von fortfahren , stimme ich

Sollten wir andererseits dasselbe tun, wenn wir bei der aktuellen Lösung bleiben?

Ich habe mich auch gefragt, ob wir davon profitieren könnten, ein Schema an den RestLink zu binden, damit wir daraus den Typnamen ableiten können.

Wenn wir ein Schema wie dieses hätten:

type Schedule {
  start: Float
  end: Float
}
type Task {
  schedule: Schedule!
  name: String!
}

Beim Ausführen der Abfrage konnten wir dann überprüfen, ob der Typ Task tatsächlich im Schema definiert ist und das verschachtelte Objekt schedule den Typnamen aus dem im Schema definierten Typ abgeleitet hat.

query MyQuery {
  task @rest(type: "Task", path: "task/") {
    name
    schedule {
      start
      end
    }
  }
}

Könnte so etwas funktionieren?

Ich mag den Ansatz, bei dem sich zum Beispiel alles, was Sie schreiben sollten, in einer Abfrage befindet

query MyQuery {
  action @rest(type: "Action", path: "action/") {
    id
    name
    kind @type(name: "ActionKind") {
      name
    }
  }
}

Für Neulinge ist es viel klarer.

Die Definition von Typen, wie Sie sie vorschlagen, kann auch für jemanden, der graphQL lernt, verwirrend sein, da dies eher serverseitig zu tun ist.

Ich mag dieses Gespräch

@sabativi , danke für deinen Beitrag!

Ich habe mit dieser Bibliothek herumgespielt und bin immer noch der Meinung, dass wir versuchen sollten, alles Mögliche in die Abfrage zu schreiben. Es gibt jedoch Fälle, in denen dies nicht möglich ist.

Wenn Sie beispielsweise ein polymorphes verschachteltes Objekt haben, dessen Typ von einer Eigenschaft angegeben wird, müssen Sie komplexe Operationen ausführen können. Daher hilft der TypePatcher wirklich, es sei denn, wir haben eine andere Lösung.

Beispiel, bei dem sich der Satz von Attributen je nach Typeigenschaft ändern kann:

{
  animals: [
    {
      type: 'cat',
      attributes: {
          ....
      },
    },
    {
      type: 'dog',
      attributes: {
          ....
      },
    },
  ],
}

Das Problem, alles in Ihre Abfrage einzubetten, besteht darin, dass Sie sich überall wiederholen müssen.

Beispiel: Auch wenn ein Benutzerobjekt in N APIs eingebettet sein kann, müssen Sie die Typanmerkungen für jeden Unterteil von Benutzer in jeder Abfrage mit einem Benutzer anhängen. Wenn Sie currentUser, buddylist, RecommendedList, nearUsers als Abfragen haben und der Benutzer hat Adressen, linkedAccounts, appData als Untermodelle, dann müssen Sie 4 x 3 = 12 Typanmerkungen schreiben – und das unter der Annahme, dass jede Abfrage nur einmal gerendert wird! Mit dem derzeit implementierten typePatcher müssen Sie nur einmal pro Teilmodell patchen.

Das Verdammendste an der Anmerkung @type ist, dass jeder Benutzer die Anmerkung kopieren und neu schreiben muss, während sie mit ihrer Abfrage experimentieren und ihre eingegebene Eigenschaftsauswahl hinzufügen oder entfernen. Dies wäre ein großer Reibungspunkt für die Verwendung.

Um es klar zu sagen, es macht mir nichts aus, wenn wir die Anmerkung hinzufügen – dies wäre großartig für sehr leichte REST-API-Injektionen. — wenn Sie nur einen oder zwei kleine REST-Endpunkte umschließen möchten. Ich wäre jedoch enttäuscht, wenn dies die einzige oder empfohlene Methode zum Umschließen von APIs wäre. Da wir Link-Rest als ersten Schritt für Leute empfehlen, die APIs in voller Größe zu GraphQL migrieren, würde die Förderung dieses Musters wahrscheinlich eine nicht triviale Anzahl von Leuten insgesamt abschrecken!

Ich stimme völlig mit Ihnen. Wir sollten beide Ansätze beibehalten und, wenn praktikabel, interagieren lassen.

Ich habe angefangen, mit einer möglichen Implementierung herumzuspielen, um dieses Verhalten zu ermöglichen. Ich bin mir jedoch nicht sicher, ob ich den besten Ansatz verfolge.

Im Moment ist es in der Lage, den Typnamen jedes verschachtelten Objekts und Arrays (von Arrays) von Objekten hinzuzufügen, das die Annotation @type(name: "Type") hinzufügt.

Wenn TypePatcher einem mit Anmerkungen versehenen Objekt einen Typnamen hinzufügt, wird dieser durch die Anmerkung ersetzt. Wir können diese Aktion ändern, indem wir sie überspringen, wenn bereits ein Typname festgelegt ist.

Leider konnte ich keine Implementierung bereitstellen, die das Hinzufügen eines annotierten Typs ermöglicht, der als nächstes vom TypePatcher verarbeitet wird. Wie, wenn ich das richtig verstanden, wirkt die TypePatcher vor Ich bin zu analysieren , ob es irgendwelche @type Anmerkungen sind. Aus diesem Grund erhalten wir das Ersetzungsverhalten kostenlos.

Sie können es hier überprüfen. Ich habe Testfälle hinzugefügt, die zeigen, was ich zuvor erwähnt habe.

@fbartho Mehrere Abfragen desselben Typs können mit Fragmenten gelöst werden. Sie können einfach ein einzelnes Fragment angeben, das alle @type(name: "Type") Anmerkungen hinzufügt, und die Benutzer können die Fragmente einfach einfügen.

@pyros2097 Die Verwendung gemeinsamer Fragmente zur Verbesserung von Abfragen mit @type ist eine clevere Idee, die ich nicht in Betracht gezogen hatte.

  • Wie genau würden Sie dieses Fragment freigeben, damit es von allen Abfragen verwendet werden kann?
  • Was passiert, wenn zwei Richtlinien kollidieren?

So wollte ich apollo-link-rest verwenden. Aber es scheitert an tief verschachtelten Typen. Ex: Es wirft einen Fehler für Bild {url} sagen kann __typename nicht lesen für ‚url‘ , die die @type Richtlinie lösen würde.

export const UserFragment = gql`
  fragment UserFragment on User {
    id
    first_name
    last_name
    image @type(name: "Image") {
      ...ImageFragment
    }
  }
`;

export const ImageFragment = gql`
  fragment ImageFragment on Image {
    url
    width
    height
  }
`;

export const AdventureFragment = gql`
  fragment AdventureFragment on Adventure {
    id
    name
    user @type(name: "User") {
      ...UserFragment
    }
    cover_photo @type(name: "Image") {
      ...ImageFragment
    }
    created_at
    updated_at
  }
`;

export const GetUserQuery = gql`
  query UserAdventures($page: Int!) {
    user @rest(method: "GET", path: "/api/current", type: "User") {
      id @export(as: "id")
      ...UserFragment
      adventures(page: $page) @rest(method: "GET",  path: "/api/adventures", params: { id: $id }, type: "Adventure") {
        ...AdventureFragment
      }
    }
  }
  ${ImageFragment}
  ${UserFragment}
  ${AdventureFragment}
`;
  • Es ist einfach, das Fragment freizugeben, Sie müssen es nur separat deklarieren und in die Abfrage einfügen, in der Sie es verwenden möchten.
  • Wenn 2 Direktiven kollidieren, vermute ich, dass die letzte Direktive/das letzte Fragment mit dieser Direktive Vorrang hat. Werde dies überprüfen müssen.

@pyros2097 – welche JSON-Daten übergeben Sie Ihrer Antwort? Ihr Beispiel sieht gut aus, aber es sollte nicht versuchen, ein ___typename aus url wenn url nur ein String ist?

url ist nur eine Zeichenfolge, aber es gibt eine Warnung für alle Schlüssel im Bildfragment. Außerdem sagt es mir, dass ich den IntrospectionFragmentMatcher von apollo-inmemory-cache verwenden soll. Dies könnte ein apollo-Inmemory-Cache sein, der die Typen für das Caching nicht identifizieren konnte und nichts mit apollo-link-rest zu tun hat.

fragmentMatcher: By default, the InMemoryCache uses a heuristic fragment matcher.
If you are using
fragments on unions and interfaces, you will need to use an IntrospectionFragmentMatcher.
For more
information, please read [our guide to setting up fragment matching for unions & interfaces].

@sky-franciscogoncalves Bitte zögern Sie nicht, Ihre @type() Anmerkung als PR an dieses Repo zu senden, damit wir sie direkt besprechen können, ohne sie weiter in dieser Diskussion über die "automatische" Typnameninferenz zu verwickeln.

In diesem Thread gab es seit Februar keine Aktion mehr, und wir haben #72 separat geöffnet, um die manuelle @type(name: …) Direktive zu implementieren. Bitte öffnen Sie erneut, wenn Sie weiterhin Techniken für eine wirklich "automatische" Typnamen-Inferenz diskutieren möchten - insbesondere, wenn uns eine sichere Standardmethode dafür einfällt. -- Ich denke, unsere Diskussion hat diese Technik nicht gefunden.

Hallo @fbartho! Ich weiß, dass dieser Thread für eine ganze Weile geschlossen ist, aber wie ich bereits sagte, gibt es noch keine Möglichkeit, Typnamen einfach abzuleiten. Obwohl die Anmerkung @type sehr schön für Experimente ist, lässt sie sich nicht skalieren. Und obwohl der in #55 entwickelte funktionale Typepatcher eine sehr schöne Alternative ist, fand ich ihn immer noch etwas zu ausführlich, um eine große API zu schreiben.
Auf diese Weise würden mich Meinungen zu einer Lib, die ich zum einfachen Patchen einer großen API erstellt habe, sehr interessieren. Es heißt apollo-type-patcher und hier ist eine Codesandbox-Demo

Für alle, die diesen Thread finden und mit einem JSON-API- kompatiblen Dienst arbeiten, habe ich den JSON-API-Link abgezweigt, um automatisch Typen für Ressourcen abzuleiten. Es bietet auch eine bequeme Beziehungsabflachung.

@mpgon & @Rsullivan00 Ich würde das Hinzufügen eines Abschnitts #### Related Libraries mit einem Link und einer einzeiligen Beschreibung Ihrer Bibliotheken zu unserer README unterstützen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

i-Hun picture i-Hun  ·  4Kommentare

karensg picture karensg  ·  5Kommentare

timhwang21 picture timhwang21  ·  7Kommentare

dphaener picture dphaener  ·  5Kommentare

kevinrobayna picture kevinrobayna  ·  6Kommentare