Conversor de Protobuf para GraphQL
Cole um esquema .proto. Receba GraphQL SDL com types, enums, listas não nulas e entry types sintéticos para campos map — pronto para servir de base ao esquema de uma gateway.
Entrada (esquema .proto)
Saída (GraphQL SDL)
O que esta ferramenta faz
Você tem um esquema Protocol Buffers e uma gateway GraphQL na frente (ou está prestes a montar uma). Ferramentas como o rejoiner do Google ligam mensagens proto a um endpoint GraphQL ao vivo em runtime, mas para esboçar o esquema, fazer revisão de código ou semear uma camada de resolvers escrita à mão, geralmente você só quer ver como fica o SDL. Este conversor faz isso no seu navegador — cole, copie a saída, jogue dentro de schema.graphql.
Cada message vira um type GraphQL; cada enum vira um enum GraphQL. Os nomes de campos são convertidos de snake_case (convenção do proto) para camelCase (convenção codificada na spec do GraphQL e exigida por todo linter). Escalares e enums são emitidos como não nulos (String!, OrderStatus!) porque os campos do proto3 sempre têm valor no fio — mesmo os não definidos caem no valor zero. Mensagens aninhadas singulares são emitidas como nullable, o que combina com a semântica has-value do proto3.
Campos repeated saem como [T!]! — lista não nula de elementos não nulos — porque campos repeated do proto3 nunca são nulos e nunca contêm entradas nulas. Maps precisam de uma gambiarra: GraphQL não tem tipo map nativo, então map<string, string> em um campo metadata vira [OrderMetadataEntry!]! com um type OrderMetadataEntry { key: String! value: String! } sintético, o mesmo padrão usado pelo Apollo e pela maioria das gateways proto-para-GraphQL em produção. Toda a conversão roda no cliente; nada do seu esquema sai da página.
Como usar
Três passos. A saída fica pronta para colar em qualquer servidor GraphQL que aceite SDL.
Cole seu esquema .proto
Solte o esquema no editor da esquerda. syntax = "proto3"; no topo é opcional — o parser dá conta de blocos message aninhados, declarações enum, oneof, map<K, V> e opções de campo. Imports são reconhecidos mas pulados, então cole os tipos importados inline se você depende deles.
A conversão de nome de campo é automática: order_id no .proto vira orderId no GraphQL. Nomes de tipo e de enum continuam em PascalCase. Campos map ganham um entry type sintético chamado <Pai><Campo>Entry.
Leia a saída
À direita: SDL GraphQL com enums primeiro, depois entry types dos maps, e por fim os messages na ordem de declaração. Tipos aninhados vêm antes dos pais, então o arquivo lê de cima para baixo. Jogue num arquivo .graphql que seu servidor carregue via graphql-tools ou buildSchema.
Conecte os resolvers
O esquema te dá o formato; ainda é preciso escrever os resolvers (ou gerá-los a partir do seu serviço gRPC). Para um caminho rápido, aponte um Apollo Server para esse SDL com resolvers stub e depois substitua cada stub por uma chamada ao seu backend gRPC. Ajuste a nullability se o contrato em runtime for diferente dos defaults do proto3.
Quando isso realmente economiza tempo
Subir uma gateway GraphQL sobre um backend gRPC
Seu time tem um serviço gRPC e o produto quer um endpoint GraphQL para o cliente web. Você precisa de um esquema inicial para discutir com o pessoal de frontend antes de escrever qualquer resolver. Cola o proto, copia o SDL, joga num doc — pronto.
Revisar uma mudança de API Protobuf
Um colega de backend adicionou campos a um message. Você quer ver como isso afeta a superfície pública do GraphQL sem rodar de novo todo o pipeline de codegen. Cola o novo .proto, faz diff do SDL contra seu esquema atual, deixa um comentário focado de revisão.
Documentação e discussões de design
Você está escrevendo uma RFC sobre um novo serviço gRPC que mais tarde vai precisar de um GraphQL na frente. Colocar as duas formas no documento — proto de um lado, SDL do outro — torna a conversa concreta. Este conversor te entrega o lado SDL sem subir um build.
Migrar de REST ou gRPC para GraphQL
Você herdou um serviço definido em Protobuf e o brief novo do produto pede uma API GraphQL. Gere aqui um rascunho de esquema como ponto de partida da migração e depois itere à mão sobre nomes de campo, nullability e paginação.
Dúvidas comuns
Meu esquema é enviado para algum lugar?
Não. O parser do .proto e o emissor do SDL rodam ambos como JavaScript no seu navegador. Abra o DevTools e olhe a aba Network enquanto cola — zero requisições. Útil quando seu esquema inclui nomes de tipo internos, caminhos de pacote ou qualquer coisa que você não queira mandar para um serviço de terceiros.
Por que inteiros de 64 bits viram String?
O escalar embutido Int do GraphQL é assinado de 32 bits pela spec do GraphQL, o que não dá para guardar um int64 ou uint64 do proto. A saída convencional é definir um escalar customizado (geralmente chamado BigInt, Long ou Int64) e serializar o valor como string. Este conversor emite String para todos os tipos inteiros de 64 bits para que o esquema já saia válido; troque essas ocorrências pelo nome do seu escalar customizado depois de defini-lo no servidor.
Como os campos map são tratados?
GraphQL não tem tipo map nativo — não existe a forma { String: String }. A saída padrão é desenrolar o map em uma lista de pares { key, value }. Então map<string, string> metadata = 8; em um message Order vira metadata: [OrderMetadataEntry!]! com um type OrderMetadataEntry { key: String! value: String! } sintético. O nome do entry type vem do message pai + nome do campo em PascalCase + Entry, que é a convenção usada pela maioria das gateways proto-para-GraphQL, incluindo o rejoiner.
Como o oneof é tratado?
Cada campo de um oneof é emitido como um campo nullable normal com um comentário marcando o grupo. GraphQL não tem um conceito nativo de união discriminada que mapeie limpinho para o oneof do proto — o análogo mais próximo é um union type customizado ou uma diretiva @oneOf de input (recém adicionada à spec para inputs). Para tipos de saída, a maioria dos esquemas só emite cada caso do oneof como nullable e documenta a restrição, que é o que fazemos aqui. Edite a saída na mão se quiser union types estritos.
Por que escalares são não nulos mas messages são nullable?
Isso bate com a semântica do proto3. Escalares no proto3 sempre têm valor no fio — um campo string não definido cai em "" por padrão, um int32 não definido cai em 0. Não dá para distinguir "definido com valor padrão" de "não definido" para escalares sem usar optional ou tipos wrapper. Já mensagens aninhadas singulares têm semântica has-value — o field tag pode estar totalmente ausente — então elas mapeiam naturalmente para campos GraphQL nullable. Campos repeated são sempre listas não nulas de elementos não nulos, de novo igual ao proto3.
E quanto a google.protobuf.Timestamp e outros well-known types?
Well-known types saem como String por padrão, já que o GraphQL não tem um escalar DateTime embutido. A maioria dos esquemas em produção define um escalar DateTime customizado (ou ISO8601String) e troca as ocorrências de String depois da geração. O mesmo vale para google.protobuf.Duration, Any e Value — emite como String primeiro, troca por escalares customizados depois.
Campos bytes — por que String?
GraphQL não tem escalar binário nativo. A codificação convencional é base64 num campo String, que é o que o mapeamento JSON do proto3 também usa para bytes. Se quiser tipagem mais estrita, defina um escalar customizado Base64String no servidor e substitua String nesses campos.
Ferramentas relacionadas
Se você trabalha com Protobuf, GraphQL e JSON, estas combinam bem: