Protobuf zu GraphQL Konverter
.proto-Schema einfügen. GraphQL SDL mit Types, Enums, nicht-nullbaren Listen und synthetischen Entry-Typen für Map-Felder herausbekommen — bereit als Startpunkt für ein Gateway-Schema.
Eingabe (.proto-Schema)
Ausgabe (GraphQL SDL)
Was dieses Tool macht
Du hast ein Protocol-Buffers-Schema und davor ein GraphQL-Gateway (oder bist gerade dabei, eins zu bauen). Tools wie Googles rejoiner verkabeln Proto-Messages zur Laufzeit mit einem laufenden GraphQL-Endpoint, aber wenn du das Schema skizzierst, einen Code-Review machst oder eine handgeschriebene Resolver-Schicht aufsetzt, willst du meistens einfach nur sehen, wie das SDL aussieht. Dieser Konverter macht genau das im Browser — einfügen, Ausgabe kopieren, in schema.graphql packen.
Jedes message wird zu einem GraphQL-type; jedes enum wird zu einem GraphQL-enum. Feldnamen werden von snake_case (Proto-Konvention) zu camelCase (die Konvention, die in der GraphQL-Spezifikation festgehalten und von jedem Linter erzwungen wird) konvertiert. Skalare und Enums werden non-null ausgegeben (String!, OrderStatus!), weil proto3-Felder auf der Leitung immer einen Wert haben — selbst nicht gesetzte fallen auf den Nullwert. Einzelne verschachtelte Messages werden nullable ausgegeben, was zur has-value-Semantik von proto3 passt.
repeated-Felder rendern als [T!]! — nicht-nullbare Liste mit nicht-nullbaren Elementen — weil repeated-Felder in proto3 niemals null sind und keine null-Einträge enthalten. Maps brauchen einen Workaround: GraphQL hat keinen nativen Map-Typ, also wird map<string, string> auf einem Feld metadata zu [OrderMetadataEntry!]! mit einem synthetischen type OrderMetadataEntry { key: String! value: String! } — dasselbe Muster, das Apollo und die meisten produktiven Proto-zu-GraphQL-Gateways nutzen. Die ganze Konvertierung läuft client-seitig; nichts von deinem Schema verlässt die Seite.
So benutzt du es
Drei Schritte. Die Ausgabe ist bereit, in jeden GraphQL-Server eingefügt zu werden, der SDL akzeptiert.
Dein .proto-Schema einfügen
Schmeiß das Schema in den linken Editor. syntax = "proto3"; oben ist optional — der Parser kommt mit verschachtelten message-Blöcken, enum-Deklarationen, oneof, map<K, V> und Feldoptionen klar. Imports werden erkannt, aber übersprungen, also füge importierte Typen inline ein, wenn du auf sie angewiesen bist.
Die Feldname-Konvertierung passiert automatisch: order_id in .proto wird zu orderId in GraphQL. Type- und Enum-Namen bleiben PascalCase. Map-Felder bekommen einen synthetischen Entry-Typ namens <Eltern><Feld>Entry.
Die Ausgabe lesen
Rechts: GraphQL SDL mit Enums zuerst, dann Map-Entry-Typen, dann Messages in Deklarationsreihenfolge. Verschachtelte Typen kommen vor ihren Eltern, damit sich die Datei von oben nach unten lesen lässt. Pack es in eine .graphql-Datei, die dein Server über graphql-tools oder buildSchema lädt.
Resolver verdrahten
Das Schema gibt dir die Form; die Resolver musst du noch schreiben (oder aus deinem gRPC-Service generieren). Für den schnellen Weg richte einen Apollo Server mit Stub-Resolvern auf dieses SDL aus und ersetze dann jeden Stub durch einen Aufruf an dein gRPC-Backend. Pass die Nullbarkeit an, wenn dein Laufzeitvertrag von den proto3-Defaults abweicht.
Wann das wirklich Zeit spart
Ein GraphQL-Gateway über einem gRPC-Backend hochziehen
Dein Team hat einen gRPC-Service und Product will einen GraphQL-Endpoint für den Webclient. Du brauchst ein Ausgangsschema, um es mit den Frontend-Leuten zu besprechen, bevor irgendwelche Resolver geschrieben werden. Proto einfügen, SDL kopieren, in ein Doc packen — fertig.
Eine Protobuf-API-Änderung reviewen
Ein Backend-Kollege hat einem Message Felder hinzugefügt. Du willst sehen, wie das die öffentliche GraphQL-Oberfläche verändert, ohne die ganze Codegen-Pipeline neu zu fahren. Neues .proto einfügen, SDL-Ausgabe gegen dein aktuelles Schema diffen, einen gezielten Review-Kommentar hinterlassen.
Doku und Design-Diskussionen
Du schreibst eine RFC zu einem neuen gRPC-Service, der irgendwann einen GraphQL-Vorbau braucht. Beide Formen ins Doc packen — proto auf der einen, SDL auf der anderen Seite — macht das Gespräch konkret. Dieser Konverter liefert dir die SDL-Seite, ohne dass du einen Build hochfährst.
Migration von REST oder gRPC zu GraphQL
Du hast einen Protobuf-definierten Service geerbt und das neue Produktbriefing fordert eine GraphQL-API. Erzeuge hier ein Schema-Draft als Startpunkt für die Migration und iteriere dann von Hand über Feldnamen, Nullbarkeit und Pagination.
Häufige Fragen
Wird mein Schema irgendwohin gesendet?
Nein. Der .proto-Parser und der SDL-Emitter laufen beide als JavaScript in deinem Browser. Mach die DevTools auf und schau in den Network-Tab, während du einfügst — null Requests. Praktisch, wenn dein Schema interne Typnamen, Package-Pfade oder anderes Zeug enthält, das du nicht an einen Drittservice schicken willst.
Warum sind 64-Bit-Ints als String typisiert?
Der eingebaute Int-Skalar von GraphQL ist laut GraphQL-Spec ein vorzeichenbehafteter 32-Bit-Wert, der einen proto-int64 oder uint64 nicht halten kann. Der übliche Workaround ist, einen Custom-Scalar zu definieren (oft BigInt, Long oder Int64 genannt) und den Wert als String zu serialisieren. Dieser Konverter gibt für alle 64-Bit-Integer-Typen String aus, damit das Schema sofort gültig ist; ersetze diese Vorkommen durch deinen Custom-Scalar-Namen, sobald du ihn auf dem Server definiert hast.
Wie werden Map-Felder behandelt?
GraphQL hat keinen nativen Map-Typ — es gibt keine { String: String }-Form. Der Standard-Workaround ist, die Map zu einer Liste von { key, value }-Paaren auszurollen. Also wird map<string, string> metadata = 8; auf einem message Order zu metadata: [OrderMetadataEntry!]! mit einem synthetischen type OrderMetadataEntry { key: String! value: String! }. Der Entry-Typ-Name wird aus dem Eltern-Message + Feldname in PascalCase + Entry abgeleitet, was die Konvention der meisten Proto-zu-GraphQL-Gateways ist, einschließlich rejoiner.
Wie wird oneof behandelt?
Jedes Feld eines oneof wird als ganz normales nullable-Feld mit einem Kommentar emittiert, der die Gruppe markiert. GraphQL hat kein natives Konzept einer diskriminierten Union, das sauber auf proto-oneof abbildet — am nächsten kommt ein Custom-Union-Type oder eine @oneOf-Input-Direktive (vor kurzem für Inputs in die Spec aufgenommen). Für Output-Typen geben die meisten Schemas einfach jeden oneof-Fall als nullable raus und dokumentieren die Einschränkung, und so machen wir es hier auch. Bearbeite die Ausgabe von Hand, wenn du strikte Union-Types willst.
Warum sind Skalare non-null, aber Messages nullable?
Das passt zur proto3-Semantik. Skalare in proto3 haben auf der Leitung immer einen Wert — ein nicht gesetztes string-Feld fällt auf "", ein nicht gesetztes int32 fällt auf 0. Es gibt keinen Weg, "auf Default gesetzt" von "nicht gesetzt" zu unterscheiden, ohne optional oder Wrapper-Typen zu nutzen. Einzelne verschachtelte Messages haben dagegen has-value-Semantik — der Field-Tag kann komplett fehlen — und mappen daher natürlich auf nullable GraphQL-Felder. repeated-Felder sind immer non-null-Listen mit non-null-Elementen, auch das passt zu proto3.
Was ist mit google.protobuf.Timestamp und anderen Well-Known-Typen?
Well-Known-Typen werden standardmäßig als String ausgegeben, da GraphQL keinen eingebauten DateTime-Skalar hat. Die meisten produktiven Schemas definieren einen Custom-DateTime- (oder ISO8601String-)Skalar und ersetzen die String-Vorkommen nach der Generierung. Dasselbe gilt für google.protobuf.Duration, Any und Value — erst als String ausgeben, später durch Custom-Scalars ersetzen, sobald sie definiert sind.
bytes-Felder — warum String?
GraphQL hat keinen nativen Binär-Skalar. Die übliche Kodierung ist base64 in einem String-Feld, was auch das proto3-JSON-Mapping für bytes nutzt. Wenn du strengere Typisierung willst, definiere serverseitig einen Base64String-Custom-Scalar und ersetze String auf den entsprechenden Feldern.
Verwandte Tools
Wenn du mit Protobuf, GraphQL und JSON arbeitest, passen diese gut dazu: