Conversor de Protobuf a GraphQL
Pega un esquema .proto. Obtén GraphQL SDL con tipos, enums, listas no nulas y tipos de entrada sintéticos para campos map — listo para sembrar el esquema de un gateway.
Entrada (esquema .proto)
Salida (GraphQL SDL)
Qué hace esta herramienta
Tienes un esquema de Protocol Buffers y un gateway de GraphQL delante (o estás a punto de construirlo). Herramientas como rejoiner de Google conectan mensajes proto a un endpoint GraphQL en vivo en tiempo de ejecución, pero para esbozar el esquema, hacer revisiones de código o sembrar una capa de resolvers escrita a mano, normalmente solo quieres ver cómo queda el SDL. Este conversor lo hace en tu navegador — pega, copia la salida, suéltala en schema.graphql.
Cada message se convierte en un type de GraphQL; cada enum se convierte en un enum de GraphQL. Los nombres de campos se convierten de snake_case (convención proto) a camelCase (la convención codificada en la especificación GraphQL y exigida por todo linter). Los escalares y enums se emiten como no nulos (String!, OrderStatus!) porque los campos de proto3 siempre tienen un valor en el wire — incluso los no establecidos toman el valor por defecto. Los mensajes anidados singulares se emiten como nulables, lo que coincide con la semántica has-value de proto3.
Los campos repeated se renderizan como [T!]! — lista no nula de elementos no nulos — porque los campos repeated en proto3 nunca son null y nunca contienen entradas null. Los maps necesitan una solución alternativa: GraphQL no tiene tipo map nativo, así que map<string, string> en un campo metadata se convierte en [OrderMetadataEntry!]! con un type OrderMetadataEntry { key: String! value: String! } sintético, el mismo patrón que usa Apollo y la mayoría de gateways proto-a-GraphQL en producción. Toda la conversión se ejecuta en el cliente; nada de tu esquema sale de la página.
Cómo usarlo
Tres pasos. La salida está lista para pegar en cualquier servidor GraphQL que acepte SDL.
Pega tu esquema .proto
Suelta el esquema en el editor de la izquierda. syntax = "proto3"; al inicio es opcional — el parser maneja bloques message anidados, declaraciones enum, oneof, map<K, V> y opciones de campo. Las imports se reconocen pero se omiten, así que pega los tipos importados en línea si dependes de ellos.
La conversión de nombres de campo es automática: order_id en .proto se convierte en orderId en GraphQL. Los nombres de tipos y enums se mantienen en PascalCase. Los campos map obtienen un tipo de entrada sintético llamado <Padre><Campo>Entry.
Lee la salida
A la derecha: SDL de GraphQL con los enums primero, luego los tipos de entrada de los maps, y luego los messages en orden de declaración. Los tipos anidados van antes que sus padres, así el archivo se lee de arriba abajo. Suéltalo en un archivo .graphql que tu servidor cargue mediante graphql-tools o buildSchema.
Conecta los resolvers
El esquema te da la forma; aún tienes que escribir los resolvers (o generarlos desde tu servicio gRPC). Para una vía rápida, apunta un Apollo Server a este SDL con resolvers stub, y luego reemplaza cada stub por una llamada a tu backend gRPC. Ajusta la nulabilidad si tu contrato en runtime difiere de los valores por defecto de proto3.
Cuándo realmente ahorra tiempo
Arrancar un gateway GraphQL sobre un backend gRPC
Tu equipo tiene un servicio gRPC y producto quiere un endpoint GraphQL para el cliente web. Necesitas un esquema de partida para discutir con los frontend antes de escribir resolvers. Pega el proto, copia el SDL, ponlo en un doc — listo.
Revisar un cambio de API en Protobuf
Un compañero de backend añadió campos a un message. Quieres ver cómo afecta a la superficie pública de GraphQL sin volver a correr todo el pipeline de codegen. Pega el nuevo .proto, haz diff del SDL contra tu esquema actual, deja un comentario de revisión enfocado.
Documentación y discusiones de diseño
Estás escribiendo un RFC sobre un nuevo servicio gRPC que eventualmente necesitará un GraphQL por delante. Embeber ambas formas en el documento — proto a un lado, SDL al otro — hace la conversación concreta. Este conversor te da el lado SDL sin levantar un build.
Migrar de REST o gRPC a GraphQL
Heredaste un servicio definido en Protobuf y el nuevo brief de producto pide una API GraphQL. Genera aquí un borrador de esquema como punto de partida para la migración, y luego itera a mano sobre nombres de campos, nulabilidad y paginación.
Preguntas frecuentes
¿Se envía mi esquema a algún sitio?
No. El parser de .proto y el emisor de SDL se ejecutan como JavaScript en tu navegador. Abre las DevTools y mira la pestaña Network mientras pegas — cero peticiones. Útil cuando tu esquema incluye nombres de tipos internos, rutas de paquete o cualquier cosa que no querrías mandar a un servicio de terceros.
¿Por qué los enteros de 64 bits salen como String?
El escalar Int incorporado de GraphQL es de 32 bits con signo según la especificación GraphQL, lo que no puede contener un int64 o uint64 de proto. La solución convencional es definir un escalar personalizado (a menudo llamado BigInt, Long o Int64) y serializar el valor como string. Este conversor emite String para todos los enteros de 64 bits, así el esquema es válido de salida; reemplaza esas ocurrencias por el nombre de tu escalar personalizado una vez que lo hayas definido en el servidor.
¿Cómo se manejan los campos map?
GraphQL no tiene tipo map nativo — no existe la forma { String: String }. La solución estándar es desplegar el map en una lista de pares { key, value }. Así, map<string, string> metadata = 8; en un message Order se convierte en metadata: [OrderMetadataEntry!]! con un type OrderMetadataEntry { key: String! value: String! } sintético. El nombre del tipo de entrada se deriva del message padre + nombre del campo en PascalCase + Entry, que es la convención que usan la mayoría de gateways proto-a-GraphQL incluyendo rejoiner.
¿Cómo se maneja oneof?
Cada campo de un oneof se emite como un campo nulable normal con un comentario marcando el grupo. GraphQL no tiene un concepto nativo de unión discriminada que mapee limpiamente al oneof de proto — el análogo más cercano es un union type personalizado o una directiva @oneOf de input (añadida recientemente a la spec para inputs). Para tipos de salida, la mayoría de esquemas simplemente emiten cada caso del oneof como nulable y documentan la restricción, que es lo que hacemos aquí. Edita la salida a mano si quieres union types estrictos.
¿Por qué los escalares son no nulos pero los messages son nulables?
Esto coincide con la semántica de proto3. Los escalares en proto3 siempre tienen un valor en el wire — un campo string sin establecer toma "" por defecto, un int32 sin establecer toma 0. No hay forma de distinguir "establecido al valor por defecto" de "no establecido" para escalares sin usar optional o tipos wrapper. Sin embargo, los mensajes anidados singulares sí tienen semántica has-value — el field tag puede estar completamente ausente — así que mapean naturalmente a campos GraphQL nulables. Los campos repeated son siempre listas no nulas con elementos no nulos, otra vez igual que proto3.
¿Y qué pasa con google.protobuf.Timestamp y otros tipos well-known?
Los tipos well-known se emiten como String por defecto, ya que GraphQL no tiene un escalar DateTime incorporado. La mayoría de esquemas en producción definen un escalar DateTime personalizado (o ISO8601String) y reemplazan las ocurrencias de String tras la generación. Lo mismo aplica a google.protobuf.Duration, Any y Value — emite como String primero, sustituye por escalares personalizados una vez definidos.
Campos bytes — ¿por qué String?
GraphQL no tiene un escalar binario nativo. La codificación convencional es base64 en un campo String, que es lo que usa también el mapeo JSON de proto3 para bytes. Si quieres un tipado más estricto, define un escalar personalizado Base64String en el servidor y reemplaza String en esos campos.
Herramientas relacionadas
Si trabajas con Protobuf, GraphQL y JSON, estas combinan bien: