Protobuf naar GraphQL Converter
Plak een .proto-schema. Krijg GraphQL SDL met types, enums, non-null lijsten en synthetische entry types voor map-velden — klaar om als basis voor het schema van een gateway te dienen.
Invoer (.proto-schema)
Uitvoer (GraphQL SDL)
Wat deze tool doet
Je hebt een Protocol Buffers-schema en een GraphQL-gateway ervoor (of staat op het punt er een te bouwen). Tools als Google's rejoiner verbinden proto-messages tijdens runtime aan een live GraphQL-endpoint, maar voor het schetsen van het schema, code review of het opzetten van een handgeschreven resolverlaag wil je meestal gewoon zien hoe het SDL eruitziet. Deze converter doet dat in je browser — plakken, output kopiëren, in schema.graphql gooien.
Elke message wordt een GraphQL-type; elke enum wordt een GraphQL-enum. Veldnamen worden omgezet van snake_case (proto-conventie) naar camelCase (de conventie die in de GraphQL-specificatie staat en door elke linter wordt afgedwongen). Scalars en enums worden non-null uitgevoerd (String!, OrderStatus!), omdat proto3-velden op de wire altijd een waarde hebben — zelfs niet-gezette velden vallen op de zero-waarde. Enkelvoudige geneste messages worden als nullable uitgevoerd, wat past bij de has-value-semantiek van proto3.
repeated-velden komen eruit als [T!]! — non-null lijst van non-null elementen — omdat repeated-velden in proto3 nooit null zijn en geen null-entries bevatten. Maps hebben een omweg nodig: GraphQL heeft geen native map-type, dus map<string, string> op een metadata-veld wordt [OrderMetadataEntry!]! met een synthetisch type OrderMetadataEntry { key: String! value: String! }, hetzelfde patroon dat Apollo en de meeste productie-gateways van proto naar GraphQL gebruiken. De hele conversie draait client-side; niets van je schema verlaat de pagina.
Hoe je het gebruikt
Drie stappen. De output is direct te plakken in elke GraphQL-server die SDL accepteert.
Plak je .proto-schema
Gooi het schema in de editor links. syntax = "proto3"; bovenaan is optioneel — de parser kan overweg met geneste message-blokken, enum-declaraties, oneof, map<K, V> en veld-opties. Imports worden herkend maar overgeslagen, dus plak geïmporteerde types inline als je ervan afhankelijk bent.
Veldnaamconversie gaat automatisch: order_id in .proto wordt orderId in GraphQL. Type- en enumnamen blijven PascalCase. Map-velden krijgen een synthetisch entry type met de naam <Ouder><Veld>Entry.
Lees de output
Rechts: GraphQL SDL met eerst de enums, dan de map-entry types, en daarna de messages in declaratievolgorde. Geneste types staan vóór hun ouders, zodat het bestand van boven naar beneden te lezen is. Stop het in een .graphql-bestand dat je server inlaadt via graphql-tools of buildSchema.
Resolvers koppelen
Het schema geeft je de vorm; de resolvers moet je nog schrijven (of laten genereren vanuit je gRPC-service). Voor een snel pad: richt een Apollo Server op dit SDL met stub-resolvers en vervang elke stub vervolgens door een aanroep naar je gRPC-backend. Pas de nullability aan als je runtime-contract afwijkt van de proto3-defaults.
Wanneer dit echt tijd scheelt
Een GraphQL-gateway bovenop een gRPC-backend opzetten
Je team heeft een gRPC-service en product wil een GraphQL-endpoint voor de webclient. Je hebt een startschema nodig om met de frontenders te bespreken voordat je resolvers gaat schrijven. Plak de proto, kopieer het SDL, gooi het in een doc — klaar.
Een Protobuf-API-wijziging reviewen
Een backendcollega heeft velden aan een message toegevoegd. Je wilt zien hoe dat de publieke GraphQL-oppervlakte raakt zonder de hele codegen-pipeline opnieuw te draaien. Plak het nieuwe .proto, diff de SDL-output tegen je huidige schema en laat een gerichte review-opmerking achter.
Documentatie en designdiscussies
Je schrijft een RFC over een nieuwe gRPC-service waar uiteindelijk een GraphQL voor moet komen. Beide vormen in het document zetten — proto aan de ene kant, SDL aan de andere — maakt het gesprek concreet. Met deze converter krijg je de SDL-kant zonder een build op te tuigen.
Migreren van REST of gRPC naar GraphQL
Je hebt een Protobuf-gedefinieerde service overgenomen en de nieuwe productbriefing vraagt om een GraphQL-API. Genereer hier een conceptschema als startpunt voor de migratie en itereer daarna handmatig op veldnamen, nullability en paginatie.
Veelgestelde vragen
Wordt mijn schema ergens naartoe gestuurd?
Nee. De .proto-parser en de SDL-emitter draaien beide als JavaScript in je browser. Open de DevTools en kijk naar het Network-tabblad terwijl je plakt — nul requests. Handig als je schema interne typenamen, package-paden of andere zaken bevat die je niet naar een externe service wilt sturen.
Waarom zijn 64-bit ints getypeerd als String?
De ingebouwde Int-scalar van GraphQL is volgens de GraphQL-specificatie 32-bit signed, en kan een proto-int64 of uint64 niet aan. De gebruikelijke oplossing is een custom scalar te definiëren (vaak BigInt, Long of Int64 genoemd) en de waarde als string te serialiseren. Deze converter geeft String uit voor alle 64-bit integer-types zodat het schema meteen geldig is; vervang die voorkomens door je eigen custom scalar-naam zodra je die aan de server hebt gedefinieerd.
Hoe worden map-velden afgehandeld?
GraphQL heeft geen native map-type — er is geen { String: String }-vorm. De standaardoplossing is de map uit te rollen tot een lijst van { key, value }-paren. Dus map<string, string> metadata = 8; op een message Order wordt metadata: [OrderMetadataEntry!]! met een synthetisch type OrderMetadataEntry { key: String! value: String! }. De entry-typenaam komt uit de naam van het bovenliggende message + veldnaam in PascalCase + Entry, wat de conventie is van de meeste proto-naar-GraphQL-gateways, waaronder rejoiner.
Hoe wordt oneof afgehandeld?
Elk veld van een oneof komt eruit als een gewoon nullable veld met een commentaar dat de groep markeert. GraphQL heeft geen native discriminated-union-concept dat schoon op proto-oneof past — het dichtstbij komt een custom union-type of een @oneOf-input-directive (recent toegevoegd aan de spec voor inputs). Voor outputtypes geven de meeste schema's elk oneof-geval gewoon als nullable uit en documenteren de beperking, en dat doen wij hier ook. Bewerk de output handmatig als je strikte union-types wilt.
Waarom zijn scalars non-null en messages nullable?
Dit volgt de semantiek van proto3. Scalars in proto3 hebben op de wire altijd een waarde — een niet-gezet string-veld valt op "", een niet-gezet int32 valt op 0. Er is geen manier om "op default gezet" te onderscheiden van "niet gezet" voor scalars zonder optional of wrapper-types te gebruiken. Enkelvoudige geneste messages hebben echter wel has-value-semantiek — de field tag kan helemaal ontbreken — en mappen daardoor netjes op nullable GraphQL-velden. repeated-velden zijn altijd non-null lijsten met non-null elementen, ook conform proto3.
Wat met google.protobuf.Timestamp en andere well-known types?
Well-known types worden standaard als String uitgegeven, omdat GraphQL geen ingebouwde DateTime-scalar heeft. De meeste productieschema's definiëren een custom DateTime- (of ISO8601String-)scalar en vervangen de String-voorkomens na de generatie. Hetzelfde geldt voor google.protobuf.Duration, Any en Value — eerst als String uitgeven, daarna door custom scalars vervangen zodra die er zijn.
bytes-velden — waarom String?
GraphQL heeft geen native binaire scalar. De gangbare codering is base64 in een String-veld, en dat is ook wat de JSON-mapping van proto3 voor bytes gebruikt. Wil je strakkere typering, definieer dan een custom Base64String-scalar op de server en vervang String op die velden.
Gerelateerde tools
Werk je met Protobuf, GraphQL en JSON, dan passen deze er goed bij: