Inndata (.proto-skjema)

Utdata (GraphQL SDL)

Hva dette verktøyet gjør

Du har et Protocol Buffers-skjema og en GraphQL-gateway foran (eller er i ferd med å bygge en). Verktøy som Googles rejoiner kobler proto-meldinger til en levende GraphQL-endepunkt i runtime, men når du skisserer skjemaet, gjør kodegjennomgang eller starter opp et håndskrevet resolver-lag, vil du som regel bare se hvordan SDL-en ser ut. Denne konverteren gjør akkurat det i nettleseren — lim inn, kopier utdata, slipp inn i schema.graphql.

Hver message blir til en GraphQL-type; hver enum blir til en GraphQL-enum. Feltnavn konverteres fra snake_case (proto-konvensjonen) til camelCase (konvensjonen som er kodet i GraphQL-spesifikasjonen og som hver linter håndhever). Skalarer og enums skrives ut non-null (String!, OrderStatus!) fordi proto3-felter alltid har en verdi på tråden — selv ikke-satte faller ned på null-verdien. Singulære nestede meldinger skrives ut som nullable, noe som matcher has-value-semantikken i proto3.

repeated-felter rendres som [T!]! — non-null liste med non-null elementer — fordi repeated-felter i proto3 aldri er null og aldri inneholder null-oppføringer. Maps trenger en omvei: GraphQL har ingen innebygd map-type, så map<string, string> på et metadata-felt blir til [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! } — samme mønster som Apollo og de fleste proto-til-GraphQL-gateways i produksjon bruker. Hele konverteringen kjører på klientsiden; ingenting fra skjemaet ditt forlater siden.

Slik bruker du det

Tre steg. Utdata er klar til å limes inn i hvilken som helst GraphQL-server som tar imot SDL.

1

Lim inn .proto-skjemaet ditt

Slipp skjemaet i editoren til venstre. syntax = "proto3"; øverst er valgfritt — parseren håndterer nestede message-blokker, enum-deklarasjoner, oneof, map<K, V> og felt-options. Imports gjenkjennes, men hoppes over, så lim inn importerte typer inline hvis du er avhengig av dem.

Feltnavnskonvertering er automatisk: order_id i .proto blir til orderId i GraphQL. Type- og enum-navn beholder PascalCase. Map-felter får en syntetisk entry-type med navnet <Forelder><Felt>Entry.

2

Les utdataene

Til høyre: GraphQL SDL med enums først, så map-entry-typer, deretter messages i deklarasjonsrekkefølgen. Nestede typer kommer før foreldrene sine slik at fila leses ovenfra og ned. Slipp den i en .graphql-fil som serveren din laster via graphql-tools eller buildSchema.

3

Koble til resolvere

Skjemaet gir deg formen; resolverne må du fortsatt skrive (eller generere fra gRPC-tjenesten din). For en rask vei kan du peke en Apollo Server mot dette SDL-et med stub-resolvere, og så bytte ut hver stub med et kall til gRPC-bakenden din. Juster nullbarhet hvis runtime-kontrakten din avviker fra proto3-defaultene.

Når dette faktisk sparer tid

Sette opp en GraphQL-gateway over en gRPC-bakende

Teamet ditt har en gRPC-tjeneste, og produkt vil ha et GraphQL-endepunkt for webklienten. Du trenger et utgangsskjema å diskutere med frontend-folket før du skriver noen resolvere. Lim inn protoen, kopier SDL-en, slipp i et dokument — ferdig.

Gjennomgå en Protobuf-API-endring

En backend-kollega har lagt til felter i et message. Du vil se hvordan det påvirker den offentlige GraphQL-flaten uten å kjøre hele codegen-pipelinen på nytt. Lim inn det nye .proto-et, diff SDL-utdataen mot dagens skjema og legg igjen en fokusert review-kommentar.

Dokumentasjon og designdiskusjoner

Du skriver en RFC om en ny gRPC-tjeneste som etter hvert skal ha GraphQL foran. Å ha begge formene i dokumentet — proto på den ene siden, SDL på den andre — gjør samtalen konkret. Med denne konverteren får du SDL-siden uten å sette opp en build.

Migrere fra REST eller gRPC til GraphQL

Du har arvet en Protobuf-definert tjeneste, og den nye produkt-briefen ber om et GraphQL-API. Generer et utkast til skjema her som startpunkt for migreringen, og iterer deretter for hånd på feltnavn, nullbarhet og paginering.

Vanlige spørsmål

Sendes skjemaet mitt noe sted?

Nei. Både .proto-parseren og SDL-emitteren kjører som JavaScript i nettleseren din. Åpne DevTools og følg med på Network-fanen mens du limer inn — null forespørsler. Greit når skjemaet ditt inneholder interne typenavn, package-stier eller annet du ikke vil sende til en tredjepartstjeneste.

Hvorfor skrives 64-bits ints som String?

Den innebygde Int-skalaren i GraphQL er signed 32-bits ifølge GraphQL-spesifikasjonen, og kan ikke holde et proto-int64 eller uint64. Den vanlige løsningen er å definere en custom-skalar (gjerne kalt BigInt, Long eller Int64) og serialisere verdien som en streng. Denne konverteren skriver ut String for alle 64-bits heltallsTyper slik at skjemaet er gyldig rett ut av boksen; bytt ut de forekomstene med navnet på din custom-skalar når du har definert den på serveren.

Hvordan håndteres map-felter?

GraphQL har ingen innebygd map-type — det finnes ingen { String: String }-form. Standardløsningen er å rulle map-en ut til en liste av { key, value }-par. Så map<string, string> metadata = 8; i et message Order blir til metadata: [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! }. Entry-typenavnet utledes fra forelder-messaget + feltnavnet i PascalCase + Entry, og det er konvensjonen de fleste proto-til-GraphQL-gateways bruker, inkludert rejoiner.

Hvordan håndteres oneof?

Hvert felt i en oneof skrives ut som et vanlig nullable-felt med en kommentar som markerer gruppen. GraphQL har ikke noe innebygd konsept for diskriminerte unioner som mapper rent til proto-oneof — det nærmeste er en custom union-type eller en @oneOf-input-direktiv (nylig lagt til i spesifikasjonen for inputs). For output-typer skriver de fleste skjemaer bare ut hvert oneof-tilfelle som nullable og dokumenterer betingelsen, og det er det vi gjør her. Rediger utdataen for hånd hvis du vil ha strenge union-typer.

Hvorfor er skalarer non-null mens messages er nullable?

Det matcher proto3-semantikken. Skalarer i proto3 har alltid en verdi på tråden — et ikke-satt string-felt faller ned på "", en ikke-satt int32 faller på 0. Det er ingen måte å skille "satt til default" fra "ikke satt" for skalarer uten å bruke optional eller wrapper-typer. Singulære nestede meldinger har derimot has-value-semantikk — felt-tag-en kan være helt fraværende — så de mapper naturlig til nullable GraphQL-felter. repeated-felter er alltid non-null-lister med non-null-elementer, også her i samsvar med proto3.

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

Well-known types skrives som standard ut som String, siden GraphQL ikke har en innebygd DateTime-skalar. De fleste produksjons-skjemaer definerer en custom DateTime- (eller ISO8601String-)skalar og bytter ut String-forekomstene etter generering. Det samme gjelder google.protobuf.Duration, Any og Value — skriv ut som String først, bytt mot custom-skalarer når de er definert.

bytes-felter — hvorfor String?

GraphQL har ingen innebygd binær skalar. Den vanlige kodingen er base64 i et String-felt, som også er det JSON-mappingen til proto3 bruker for bytes. Vil du ha strammere typer, definer en custom Base64String-skalar på serveren og bytt ut String på de feltene.

Relaterte verktøy

Jobber du med Protobuf, GraphQL og JSON, passer disse godt sammen: