ConnectRPC: Generate Typed SDKs from Your GraphQL API

cover
Ahmet Soormally

Ahmet Soormally

min read

TL;DR

ConnectRPC is a protocol translation layer in the Cosmo Router. Write named GraphQL operations against your federated schema, run one CLI command, and get Protocol Buffer definitions, typed SDKs (Go, TypeScript, Swift, Kotlin, etc.), and an OpenAPI spec. The router serves them as gRPC, gRPC-Web, Connect protocol, and HTTP/JSON alongside your GraphQL endpoint. No separate API layers to build or maintain. No schema changes required. Operations are the API contract; everything else is derived.

The Multi-Protocol Problem: Why GraphQL APIs Are Hard to Expose as REST or gRPC

You've built a federated GraphQL API. Your internal teams use it daily. The schema is well-governed, the subgraphs are cleanly separated, and the router handles composition and execution.

Then the requests start coming in. An external partner wants REST with an OpenAPI spec. A mobile team wants typed SDKs with compile-time safety. A backend service wants gRPC with binary encoding. Some consumers have hard platform constraints — a Roku box or embedded device can't run a GraphQL client library, needs minimal payload sizes, and may only support simple HTTP/JSON or binary protocols. These are platform limitations.

Today, you maintain separate API layers for each of these consumers. Separate codebases, separate contracts, separate deployment pipelines. Each one drifts from the others. The platform team becomes a bottleneck because every new consumption pattern requires a new integration layer.

AI coding assistants are making this worse, not better. It's never been easier to scaffold a new BFF, but every generated service needs to be reviewed, tested, deployed, secured, and maintained. Coding assistants accelerate BFF sprawl; they don't solve the underlying problem.

There's no centralized control over what operations are exposed through which protocols, making the governance problem just as real. There is no consistent contract enforcement across consumption channels and no single source of truth that a platform engineer can point to and say, "This is the API surface."

Protocol Choice Is a Deployment Concern, Not an Implementation Problem

Your GraphQL schema already defines the data model, and your named operations define the access patterns. The missing piece is an intermediate representation that can target multiple protocols from that same source of truth.

Protocol Buffers are that intermediate representation, and they solve some of the hard problems: stable field numbering for wire compatibility, cross-language code generation through the Buf ecosystem, binary encoding for performance, and idempotency annotations for HTTP caching. While GraphQL solves the other half: schema governance, federation composition, and operation-level access control.

ConnectRPC bridges the two. GraphQL is the design layer where you model your data and curate your API surface, and Protocol Buffers are the compilation target that unlocks multi-protocol serving and typed SDK generation. The router handles the translation at runtime.

No More BFF Sprawl: Why Everything Downstream Is Generated

Everything downstream of the operations is deterministic. The same operations against the same schema always produce the same proto, the same SDKs, the same OpenAPI spec. There's no hand-written glue code, no one-off adapters, nothing to maintain. The only creative work is deciding what data to expose, in what shape, for which consumer.

That's the design problem.

This changes how you use AI coding assistants day-to-day. Instead of generating one-off BFF services that accelerate sprawl, you point them at the actual design problem: "Here's my schema. I need an API surface for a mobile checkout flow — what operations should I expose?" The output is a handful of .graphql files, not a codebase. The agent helps you think consumer-first about your API surface; the deterministic toolchain handles everything from there.

That’s the difference between using AI to generate more code and using it to design an abstraction so there’s less code to maintain.

Introducing Cosmo ConnectRPC

ConnectRPC is a protocol translation layer built into the Cosmo Router. It compiles GraphQL operations to Protocol Buffer definitions and serves them as multi-protocol APIs — gRPC, REST/HTTP, and typed client SDKs — turning what would be a multi-team, multi-month integration effort into a build-time generation step.

ConnectRPC is available today in Cosmo Router v0.283.0 and on Cosmo Cloud. It works with any Cosmo-managed federated graph, no schema changes or subgraph modifications required.

How ConnectRPC Turns GraphQL Operations into gRPC, REST, and Typed SDKs

There are two halves to this: a build-time compilation pipeline and a runtime serving layer.

At build time, you compile named GraphQL operations into Protocol Buffer definitions. The Buf toolchain then generates typed SDKs and OpenAPI specs from those protos.

Loading diagram...

At runtime, the Cosmo Router serves a ConnectRPC endpoint alongside your GraphQL endpoint. It accepts requests over gRPC, gRPC-Web, Connect protocol , and plain HTTP/JSON — all resolving to the same underlying GraphQL execution. The router maps proto messages to GraphQL variables, executes the operation against your federated graph, and returns the response in whatever wire format the client requested.

Loading diagram...

How the GraphQL-to-Proto Compilation Pipeline Works

Operations as API Contracts

API surfaces are defined as named GraphQL operations. There is one per file, each version-controlled as a persisted operation (also known as named operations or trusted documents). This is API design: each operation is an intentional choice about what data to expose, in what shape, and for which consumers. Consumers can only call these curated operations; there's no way to construct arbitrary queries or discover the underlying schema.

1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7

Each file contains a single named operation. The operation name becomes the RPC method name. The operation variables become the request message fields. The selection set becomes the response message.

You're not limited to a single service. Multiple ConnectRPC services can be defined from the same federated graph, with each exposing a different domain or capability. A ProductService, OrderService, and RecommendationService can each have their own operations, proto package, and generated SDKs — all backed by the same federated graph. Alternatively, you can organize services per consumer: one for a mobile app, another for an external partner, each with its own curated API surface.

From Operations to Proto Definitions

A single CLI command compiles operations and schema into a Protocol Buffer service definition.

1
2
3
4
5
6

This produces service.proto and service.proto.lock.json. The generated proto for the operations above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

The mapping is deterministic and follows proto3 conventions.

  • GraphQL types become proto messages
  • Fields get snake_case naming
  • Nullable scalars use wrapper types, so nullability semantics are preserved
  • Enums get a type prefix and an _UNSPECIFIED zero value.

The result is a proto that feels idiomatic to gRPC engineers.

Query operations are annotated with NO_SIDE_EFFECTS, which tells the Connect protocol to serve them via HTTP GET, ensuring they are cacheable by CDNs and reverse proxies.

The lock file tracks field number assignments, so regenerating after schema changes never breaks wire compatibility with existing clients.

API evolution is where most multi-protocol systems break. Proto field numbers are part of the wire format — if they shift, deployed clients silently receive wrong data. The lock file makes this a non-issue: commit it alongside your proto, and field numbers stay stable no matter how your operations evolve.

The generation step validates operations against the current schema — if an operation references a removed or renamed field, generation fails with a specific error before any proto is produced. The lock file detects breaking changes to field number assignments and surfaces them as warnings rather than silently reordering. Problems are caught at build time, in CI, before anything reaches production.

Router Configuration

Enabling ConnectRPC in the Cosmo Router is a configuration change. You point it at your services directory, and it will serve a ConnectRPC endpoint alongside your existing GraphQL endpoint. Both listeners run inside the same router process. When operations are updated and protos regenerated, the router picks up changes atomically.

See the configuration reference for the full setup.

SDK Generation via Buf

With a proto in hand, the Buf toolchain generates typed clients for any language.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1

One command. Go client, TypeScript client, and OpenAPI spec — all generated from the same proto.

In practice, most teams run wgc grpc-service generate and buf generate as CI steps. The .graphql operation files and the lock file are committed to version control. The generated proto and SDKs are build artifacts. The generation is deterministic, so the output is reproducible across environments.

What Consumers See

Consumers pick their protocol. The router handles the rest.

TypeScript SDK:
1
2
3
4
5
6
7
8
9
10
Go SDK:
1
2
3
4
5
6
HTTP/JSON (no SDK required):
1
2
3
4
gRPC:
1
2
3
4
5

The examples above show Go, TypeScript, HTTP/JSON, and gRPC, but the Buf ecosystem supports any language with a protoc or Connect plugin. Add the plugin to your buf.gen.yaml and you have another typed client. The tutorial repository walks through several of these, including HTTP GET for cacheable queries.

Why ConnectRPC Replaces Separate API Layers

Persisted operations as the API design surface. ConnectRPC treats named GraphQL operations as the API contract — not the schema, not an auto-generated CRUD layer. Each operation is an intentional design decision: which data to expose, in what shape, for which consumer. This is both a security property and a design discipline. The platform team controls the API surface the same way a library author controls a public API: by choosing what to export.

Lock file for backward-compatible evolution. The service.proto.lock.json file tracks field number assignments across regenerations. When you modify an operation's selection set, existing field numbers are preserved and removed fields have their numbers reserved. Deployed clients keep working.

Idempotency annotations for HTTP caching. Query operations automatically get idempotency_level = NO_SIDE_EFFECTS in the proto definition. This tells the Connect protocol to use HTTP GET for these methods, making them cacheable by CDNs and HTTP caches without any additional configuration.

Atomic hot reload. The router watches the services directory and picks up proto and operation changes without restarts. Update an operation, regenerate the proto, drop the files in, and the router switches over atomically.

One source of truth. Your GraphQL schema is the data model. Your named operations are the contracts. The proto is derived, the SDKs are derived, and the OpenAPI spec is derived. There is no drift because there is nothing to drift from.

Here's what changes concretely:

Without ConnectRPCWith ConnectRPC
Protocol surfacesSeparate codebase per protocolOne set of operations, multiple outputs
Contract driftManual synchronization across layersDerived from single source — impossible to drift
API evolutionCoordinate changes across REST, gRPC, and GraphQL layersUpdate operations, regenerate, lock file preserves compatibility
Team bottleneckPlatform team builds each adapterPlatform team curates operations; SDKs are generated
Security surfaceEach layer has its own auth and validationOne execution path, one authorization model

Use Cases: Partner APIs, Mobile SDKs, and Platform Governance

External partner API

Your platform team manages a federated GraphQL API internally. External partners need REST with an OpenAPI spec and versioning guarantees. With ConnectRPC, you author a set of operations that define the partner API surface, generate the proto, and hand partners an OpenAPI spec and HTTP endpoints. The partner never sees GraphQL. The platform team never maintains a separate REST layer. When the API evolves, you update the operations, regenerate, and the lock file ensures backward compatibility.

Mobile team with typed SDKs

Your mobile engineers want compile-time type safety and binary encoding for bandwidth-sensitive environments. They don't want to adopt a GraphQL client library. With ConnectRPC, you generate a Swift or Kotlin SDK from the proto (via Buf plugins) and hand them a typed client that talks binary protobuf over HTTP/2. Request and response types are generated, autocomplete works, and the mobile team never writes a GraphQL query.

Platform governance across teams

Your organization runs 50 microservices behind a federated graph. Each domain team owns its subgraphs, but the platform team controls what's exposed externally. With ConnectRPC, the platform team authors and version-controls the operation files that define the external API surface. Domain teams evolve their subgraphs freely; the exposed API only changes when the platform team updates the operations and regenerates. There's one place to audit what's exposed, one lock file that enforces backward compatibility, and one CI pipeline that validates the entire external contract. Individual teams don't need to know ConnectRPC exists; they just maintain their subgraphs.

Performance Characteristics of ConnectRPC

ConnectRPC runs on Protocol Buffers and the Connect protocol — both have well-understood performance characteristics.

Binary encoding. Protobuf payloads are smaller than equivalent JSON and faster to serialize/deserialize — typically 2-5x smaller on the wire with lower serialization overhead. For high-throughput services or bandwidth-constrained mobile clients, the difference is significant.

HTTP GET for queries. Because query operations are annotated with NO_SIDE_EFFECTS, the Connect protocol serves them via HTTP GET. This makes query responses cacheable by CDNs, reverse proxies, and browser caches — with no application-level caching logic.

In-process transcoding. Protocol translation happens inside the router process — no external sidecar, no additional network hop. This was a deliberate design choice to avoid the latency and operational complexity of a separate transcoding service.

No runtime reflection. The proto definitions are compiled and loaded at startup. The router does not use runtime reflection to resolve proto types or field mappings.

Design Principle: Protocol Translation Belongs in Infrastructure

ConnectRPC is built on a specific belief: protocol translation belongs in infrastructure, not in application code. Teams shouldn't build and maintain separate API layers to serve the same data over different protocols. The platform should handle that.

This is the same separation of concerns that made API gateways successful for REST. ConnectRPC applies it to multi-protocol serving: GraphQL governs the schema and operations, Protocol Buffers provide the compilation target, and the router handles everything in between. Individual teams pick the protocol that fits their consumers, and the platform ensures consistency.

Standalone transcoding proxies require you to author and maintain proto definitions manually, separate from your API logic. ConnectRPC derives protos from your existing GraphQL operations. The operations are the contract, and the proto is a compilation artifact. There's nothing to keep in sync because there's only one source.

Not every consumer wants or can adopt GraphQL. Some have platform constraints, such as streaming devices, embedded systems, or partner integrations locked to REST. ConnectRPC meets them where they are without asking anyone to change their stack.

The generated artifacts are standard Protocol Buffers and standard ConnectRPC/gRPC clients. Nothing in the output is proprietary to Cosmo. If you move away from the Cosmo Router, the protos, SDKs, and OpenAPI specs remain valid and usable, because they're standard artifacts produced by the Buf toolchain.

What's on the ConnectRPC Roadmap

We're exploring several directions based on early adopter feedback:

  • Streaming support. Map GraphQL subscriptions to server-streaming RPCs for real-time data over gRPC and Connect.
  • Per-method authorization. Apply fine-grained access control at the RPC method level, building on the same @requiresScopes directives used in your GraphQL schema.
  • Per-method rate limiting. Apply rate limits at the RPC method level, with policies defined centrally in the router configuration.
  • Observability integration. Surface per-RPC-method metrics, traces, and access logs in Cosmo Studio alongside existing GraphQL analytics.
  • API reference documentation. Auto-generate human-readable API reference sites from the proto definitions — method descriptions, type tables, and usage examples — so consumers get documentation alongside their SDKs.
  • Proto registry integration. Publish generated protos to a Buf Schema Registry or internal registry as part of the generation pipeline.

If there's a capability you'd like to see, open an issue or reach out on Discord .

Get Started with ConnectRPC

ConnectRPC is available now. The documentation covers configuration, CLI reference, and SDK generation in detail.


Frequently Asked Questions (FAQ)

ConnectRPC is a protocol translation layer built into the Cosmo Router. It compiles named GraphQL operations into Protocol Buffer definitions and serves them as gRPC, REST/HTTP, and typed client SDKs — turning an existing federated GraphQL API into a multi-protocol API.

Cosmo Connect lets backend teams implement gRPC services or router plugins that the router consumes as federation subgraphs — it brings non-GraphQL services into the federated graph. ConnectRPC goes the other direction — it takes your existing federated GraphQL API and exposes curated operations through gRPC, REST, and typed SDKs for downstream consumers.

ConnectRPC supports gRPC (binary protobuf), gRPC-Web, Connect protocol (HTTP/JSON), and REST/HTTP — all from the same proto definition. The router handles protocol translation transparently, so consumers pick the protocol that fits their stack.

The lock file (service.proto.lock.json) tracks field number assignments across generations. When you regenerate protos after modifying operations, the lock file ensures that existing fields keep their numbers and removed fields have their numbers reserved. This prevents breaking wire compatibility with already-deployed clients.

Yes. ConnectRPC works with any Cosmo-managed federated graph. You author named GraphQL operations against your existing schema, run the generate command, configure the router, and you have a multi-protocol API. No schema changes or subgraph modifications required.

SDK generation uses the Buf ecosystem, so you get support for Go, TypeScript, Java, Kotlin, Swift, Python, and any other language with protoc or Buf plugins. You can also generate OpenAPI specifications from the same proto definitions for REST-oriented consumers.


Ahmet Soormally

Ahmet Soormally

Principal Solutions Engineer at WunderGraph

Ahmet is a Principal Solutions Engineer at WunderGraph, helping teams adopt Cosmo. He leads technical evaluations, builds prototypes, and runs workshops to accelerate adoption, while improving SDKs, documentation, and onboarding to enhance the developer experience.