Input (.proto-skema)

Output (GraphQL SDL)

Hvad værktøjet gør

Du har et Protocol Buffers-skema og en GraphQL-gateway foran (eller er lige ved at bygge en). Værktøjer som Googles rejoiner kobler proto-messages til en levende GraphQL-endpoint i runtime, men når du skitserer skemaet, laver code review eller starter et håndskrevet resolver-lag, vil du som regel bare se, hvordan SDL’en ser ud. Den her konverter gør det i din browser — indsæt, kopiér output, dumpe det i schema.graphql.

Hver message bliver til en GraphQL-type; hver enum bliver til en GraphQL-enum. Feltnavne konverteres fra snake_case (proto-konventionen) til camelCase (konventionen i GraphQL-specifikationen, som hver linter håndhæver). Skalarer og enums skrives non-null (String!, OrderStatus!), fordi proto3-felter altid har en værdi på tråden — selv ikke-sat falder ned på nul-værdien. Singulære nestede messages skrives som nullable, hvilket matcher proto3’s has-value-semantik.

repeated-felter renderes som [T!]! — non-null liste med non-null elementer — fordi proto3’s repeated-felter aldrig er null og aldrig indeholder null-elementer. Maps kræver et workaround: GraphQL har ingen indbygget map-type, så map<string, string> på et metadata-felt bliver til [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! } — samme mønster som Apollo og de fleste proto-til-GraphQL-gateways i produktion bruger. Hele konverteringen kører klient-side; intet fra dit skema forlader siden.

Sådan bruger du det

Tre trin. Outputtet er klar til at indsætte i en hvilken som helst GraphQL-server, der accepterer SDL.

1

Indsæt dit .proto-skema

Smid skemaet i editoren til venstre. syntax = "proto3"; i toppen er valgfrit — parseren kan håndtere nestede message-blokke, enum-deklarationer, oneof, map<K, V> og felt-options. Imports genkendes, men springes over, så indsæt importerede typer inline, hvis du afhænger af dem.

Feltnavnskonvertering sker automatisk: order_id i .proto bliver til orderId i GraphQL. Type- og enum-navne forbliver PascalCase. Map-felter får en syntetisk entry-type ved navn <Forælder><Felt>Entry.

2

Læs outputtet

Til højre: GraphQL SDL med enums først, så map-entry-typer, derefter messages i deklarationsrækkefølgen. Nestede typer kommer før deres forældre, så filen læses oppefra og ned. Smid det i en .graphql-fil, som din server indlæser via graphql-tools eller buildSchema.

3

Tilkobl resolvers

Skemaet giver dig formen; du skal stadig skrive resolvers (eller generere dem fra din gRPC-tjeneste). For en hurtig vej skal du pege en Apollo Server mod denne SDL med stub-resolvers og derefter erstatte hver stub med et kald til dit gRPC-backend. Justér nullability, hvis dit runtime-kontrakt afviger fra proto3-defaults.

Hvornår det faktisk sparer tid

Sætte en GraphQL-gateway oven på et gRPC-backend

Dit team har en gRPC-tjeneste, og produktet vil have en GraphQL-endpoint til webklienten. Du har brug for et udgangsskema at diskutere med frontend-folket, før der skrives nogen resolvers. Indsæt protoen, kopiér SDL’en, smid det i et dokument — færdig.

Gennemgå en Protobuf-API-ændring

En backend-kollega har tilføjet felter til et message. Du vil se, hvordan det påvirker den offentlige GraphQL-overflade uden at køre hele codegen-pipelinen igen. Indsæt det nye .proto, diff SDL-outputtet mod dit nuværende skema og efterlad en fokuseret review-kommentar.

Dokumentation og designdiskussioner

Du skriver en RFC om en ny gRPC-tjeneste, der senere skal have et GraphQL foran. At have begge former i dokumentet — proto på den ene side, SDL på den anden — gør samtalen konkret. Med denne konverter får du SDL-siden uden at skulle starte et build.

Migrere fra REST eller gRPC til GraphQL

Du har arvet en Protobuf-defineret tjeneste, og det nye produktbrief beder om et GraphQL-API. Generer her et udkast til skemaet som startpunkt for migreringen, og iterér derefter manuelt på feltnavne, nullability og pagination.

Almindelige spørgsmål

Bliver mit skema sendt nogen steder?

Nej. Både .proto-parseren og SDL-emitteren kører som JavaScript i din browser. Åbn DevTools og hold øje med Network-fanen, mens du indsætter — nul forespørgsler. Smart, når dit skema indeholder interne typenavne, package-stier eller andet, du ikke vil sende til en tredjepartstjeneste.

Hvorfor skrives 64-bit-int’er som String?

GraphQL’s indbyggede Int-skalar er signed 32-bit ifølge GraphQL-specifikationen, hvilket ikke kan rumme et proto-int64 eller uint64. Det konventionelle workaround er at definere en custom-skalar (ofte kaldet BigInt, Long eller Int64) og serialisere værdien som en streng. Konverteren her udskriver String for alle 64-bit-heltalstyper, så skemaet er gyldigt med det samme; udskift de forekomster med navnet på din custom-skalar, når du har defineret den på serveren.

Hvordan håndteres map-felter?

GraphQL har ingen indbygget map-type — der findes ikke en { String: String }-form. Standard-workaround er at rulle map ud til en liste af { key, value }-par. Så map<string, string> metadata = 8; på et message Order bliver til metadata: [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! }. Entry-typenavnet udledes af forælder-messaget + feltnavnet i PascalCase + Entry, hvilket er konventionen, som de fleste proto-til-GraphQL-gateways bruger, inklusive rejoiner.

Hvordan håndteres oneof?

Hvert felt i et oneof skrives ud som et helt almindeligt nullable-felt med en kommentar, der markerer gruppen. GraphQL har ikke et indbygget koncept for diskriminerede unioner, der mapper rent til proto-oneof — det nærmeste er en custom union-type eller en @oneOf-input-direktiv (for nylig tilføjet til specifikationen for inputs). For output-typer udskriver de fleste skemaer bare hvert oneof-tilfælde som nullable og dokumenterer betingelsen, hvilket er det, vi gør her. Redigér outputtet manuelt, hvis du vil have stramme union-typer.

Hvorfor er skalarer non-null, men messages nullable?

Det matcher proto3-semantikken. Skalarer i proto3 har altid en værdi på tråden — et ikke-sat string-felt falder ned på "", et ikke-sat int32 falder på 0. Der er ingen måde at skelne "sat til default" fra "ikke sat" for skalarer uden at bruge optional eller wrapper-typer. Singulære nestede messages har derimod has-value-semantik — feltets tag kan være helt fraværende — så de mapper naturligt til nullable GraphQL-felter. repeated-felter er altid non-null lister med non-null elementer, igen i tråd med proto3.

Hvad med google.protobuf.Timestamp og andre well-known types?

Well-known types udskrives som standard som String, da GraphQL ikke har en indbygget DateTime-skalar. De fleste produktions-skemaer definerer en custom DateTime- (eller ISO8601String-)skalar og udskifter String-forekomsterne efter genereringen. Samme historie for google.protobuf.Duration, Any og Value — udskriv som String først, og udskift med custom-skalarer, når de er definerede.

bytes-felter — hvorfor String?

GraphQL har ingen indbygget binær skalar. Den konventionelle kodning er base64 i et String-felt, hvilket også er det, proto3’s JSON-mapping bruger til bytes. Hvis du vil have strammere typer, definér en custom Base64String-skalar på serveren og udskift String på de felter.

Relaterede værktøjer

Arbejder du med Protobuf, GraphQL og JSON, fungerer disse godt sammen: