Indata (.proto-schema)

Utdata (GraphQL SDL)

Vad det här verktyget gör

Du har ett Protocol Buffers-schema och en GraphQL-gateway framför (eller är på väg att bygga en). Verktyg som Googles rejoiner kopplar proto-meddelanden till en levande GraphQL-endpoint i runtime, men för att skissa schemat, göra kodgranskning eller så en handskriven resolver-lager räcker det oftast att se hur SDL:en ser ut. Den här konverteraren gör det i din webbläsare — klistra in, kopiera utdata, släpp in i schema.graphql.

Varje message blir en GraphQL-type; varje enum blir en GraphQL-enum. Fältnamn konverteras från snake_case (proto-konvention) till camelCase (konventionen som finns i GraphQL-specen och som varje linter tvingar fram). Skalärer och enums skrivs ut non-null (String!, OrderStatus!) eftersom proto3-fält alltid har ett värde på tråden — även icke-satta hamnar på nollvärdet. Singulära nästlade meddelanden skrivs ut som nullable, vilket matchar proto3:s has-value-semantik.

repeated-fält renderas som [T!]! — non-null lista med non-null element — för att proto3:s repeated-fält aldrig är null och aldrig innehåller null-poster. Maps kräver en omväg: GraphQL har ingen inbyggd map-typ, så map<string, string> på ett metadata-fält blir [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! } — samma mönster som Apollo och de flesta produktions-gateway:er från proto till GraphQL använder. Hela konverteringen körs på klientsidan; inget av ditt schema lämnar sidan.

Så använder du det

Tre steg. Utdata är klar att klistra in i vilken GraphQL-server som helst som accepterar SDL.

1

Klistra in ditt .proto-schema

Släpp schemat i editorn till vänster. syntax = "proto3"; överst är valfritt — parsern hanterar nästlade message-block, enum-deklarationer, oneof, map<K, V> och fält-options. Imports känns igen men hoppas över, så klistra in importerade typer inline om du beror på dem.

Fältnamnskonverteringen är automatisk: order_id i .proto blir orderId i GraphQL. Type- och enum-namn behåller PascalCase. Map-fält får en syntetisk entry-typ med namnet <Förälder><Fält>Entry.

2

Läs utdatan

Till höger: GraphQL SDL med enums först, sedan map-entry-typer, sedan messages i deklarationsordning. Nästlade typer kommer före sina föräldrar så att filen läses uppifrån och ner. Släpp in det i en .graphql-fil som din server laddar via graphql-tools eller buildSchema.

3

Koppla in resolvers

Schemat ger dig formen; du måste fortfarande skriva resolvers (eller generera dem från din gRPC-tjänst). För en snabb väg, peka en Apollo Server mot detta SDL med stub-resolvers, och byt sedan ut varje stub mot ett anrop till ditt gRPC-backend. Justera nullbarhet om ditt runtime-kontrakt avviker från proto3-defaulterna.

När det här faktiskt sparar tid

Starta upp en GraphQL-gateway ovanför ett gRPC-backend

Ditt team har en gRPC-tjänst och produkten vill ha en GraphQL-endpoint för webbklienten. Du behöver ett startschema att diskutera med frontend-folket innan du skriver några resolvers. Klistra in protot, kopiera SDL:en, släpp in i ett dokument — klart.

Granska en Protobuf-API-ändring

En backend-kollega lade till fält i ett message. Du vill se hur det påverkar den publika GraphQL-ytan utan att köra om hela codegen-pipelinen. Klistra in det nya .proto:t, diffa SDL-utdatan mot ditt nuvarande schema och lämna en fokuserad granskningskommentar.

Dokumentation och designdiskussioner

Du skriver en RFC om en ny gRPC-tjänst som så småningom kommer behöva ett GraphQL framför. Att lägga in båda formerna i dokumentet — proto på ena sidan, SDL på andra — gör samtalet konkret. Den här konverteraren ger dig SDL-sidan utan att du behöver dra igång en build.

Migrera från REST eller gRPC till GraphQL

Du ärvde en Protobuf-definierad tjänst och den nya produktbriefen kräver ett GraphQL-API. Generera ett utkast till schema här som startpunkt för migreringen och iterera sedan för hand på fältnamn, nullbarhet och paginering.

Vanliga frågor

Skickas mitt schema någonstans?

Nej. .proto-parsern och SDL-emittern körs båda som JavaScript i din webbläsare. Öppna DevTools och kolla Network-fliken medan du klistrar in — noll requests. Användbart när schemat innehåller interna typnamn, paketsökvägar eller annat du inte vill skicka till en tredjepartstjänst.

Varför skrivs 64-bitars heltal som String?

Den inbyggda Int-skaläret i GraphQL är signed 32-bitars enligt GraphQL-specen, vilket inte rymmer ett proto-int64 eller uint64. Den vedertagna lösningen är att definiera en custom-skalär (ofta kallad BigInt, Long eller Int64) och serialisera värdet som en sträng. Den här konverteraren skriver ut String för alla 64-bitars heltalsTyper så att schemat blir giltigt direkt; byt ut de förekomsterna mot din custom-skaläres namn när du har definierat den på servern.

Hur hanteras map-fält?

GraphQL har ingen inbyggd map-typ — det finns ingen { String: String }-form. Standardlösningen är att rulla ut mapen till en lista av { key, value }-par. Så map<string, string> metadata = 8; på ett message Order blir metadata: [OrderMetadataEntry!]! med en syntetisk type OrderMetadataEntry { key: String! value: String! }. Entry-typens namn härleds från förälder-message + fältnamn i PascalCase + Entry, vilket är konventionen som de flesta proto-till-GraphQL-gateway:er använder, inklusive rejoiner.

Hur hanteras oneof?

Varje fält i ett oneof skrivs ut som ett vanligt nullable-fält med en kommentar som markerar gruppen. GraphQL har inget inbyggt koncept för diskriminerade unioner som mappar rent mot proto-oneof — närmast kommer en custom union-typ eller en @oneOf-input-direktiv (nyligen tillagd i specen för inputs). För output-typer skriver de flesta scheman bara ut varje oneof-fall som nullable och dokumenterar restriktionen, vilket är vad vi gör här. Redigera utdatan för hand om du vill ha strikta union-typer.

Varför är skalärer non-null men messages nullable?

Det matchar proto3:s semantik. Skalärer i proto3 har alltid ett värde på tråden — ett icke-satt string-fält hamnar på "", ett icke-satt int32 hamnar på 0. Det går inte att skilja "satt till default" från "icke-satt" för skalärer utan att använda optional eller wrapper-typer. Singulära nästlade messages har dock has-value-semantik — fälttaggen kan saknas helt — så de mappar naturligt till nullable GraphQL-fält. repeated-fält är alltid non-null-listor med non-null-element, även det enligt proto3.

Hur är det med google.protobuf.Timestamp och andra well-known types?

Well-known types skrivs som standard ut som String, eftersom GraphQL inte har en inbyggd DateTime-skalär. De flesta produktionsscheman definierar en custom DateTime- (eller ISO8601String-)skalär och byter ut String-förekomsterna efter generering. Samma sak gäller google.protobuf.Duration, Any och Value — skriv ut som String först, byt mot custom-skalärer när de är definierade.

bytes-fält — varför String?

GraphQL har ingen inbyggd binär skalär. Den konventionella kodningen är base64 i ett String-fält, vilket också är vad proto3:s JSON-mappning använder för bytes. Vill du ha striktare typning, definiera en custom Base64String-skalär på servern och byt ut String på de fälten.

Relaterade verktyg

Jobbar du med Protobuf, GraphQL och JSON funkar dessa bra ihop: