Skip to content

feat(grpc_datasource): add ConnectRPC transport implementation#1509

Open
fengyuwusong wants to merge 5 commits into
wundergraph:masterfrom
fengyuwusong:feat/connectrpc-datasource-impl
Open

feat(grpc_datasource): add ConnectRPC transport implementation#1509
fengyuwusong wants to merge 5 commits into
wundergraph:masterfrom
fengyuwusong:feat/connectrpc-datasource-impl

Conversation

@fengyuwusong
Copy link
Copy Markdown
Contributor

Summary

Add a ConnectRPC implementation of the RPCTransport interface introduced in #1490, so the gRPC data source can dial Connect subgraphs without changing the planner, compiler, or JSON builder. The same MockService fixture is regenerated via buf to emit Connect handlers alongside the existing gRPC code, and a new end-to-end test exercises the data source over Connect against that fixture.

This is the second of the two PRs that together replace #1480.

Related

What's changed

pkg/grpctest — buf-driven fixture

  • New buf.yaml / buf.gen.yaml: drive protoc-gen-go, protoc-gen-go-grpc, and protoc-gen-connect-go from a single buf generate invocation, keeping the existing import path (graphql-go-tools/v2/pkg/grpctest/productv1) via M= overrides.
  • Makefile: generate-proto now calls buf generate; install-protoc also installs protoc-gen-connect-go.
  • New productv1/productv1connect/product.connect.go: buf-generated Connect handler/client. Sibling of the existing productv1/product_grpc.pb.go, so the package serves Connect, gRPC, and gRPC-Web from one HTTP endpoint.
  • New mockservice_connect.go: hand-maintained adapter (MockServiceConnect) wrapping the existing MockService onto productv1connect.ProductServiceHandler. Same backing implementation, two transports.

pkg/engine/datasource/grpc_datasource — ConnectRPC transport

  • New transport_connect.go: NewConnectTransport(ConnectTransportConfig{BaseURL, Encoding, Interceptors, HTTPClient}) implements RPCTransport.Invoke on top of connectrpc.com/connect. Supports ConnectEncodingProtobuf ("proto") and ConnectEncodingJSON ("json") wire formats. Translates grpc/metadata to and from Connect headers using the same conventions as GRPCTransport, with binary request headers (-bin suffix) base64-encoded per the Connect spec.
  • The Interceptors field exposes connect-go's interceptor chain so callers can plug in tracing, logging, retries, or auth header injection without wrapping the HTTPClient.
  • The internal per-procedure client cache uses custom codecs (dynamicProtoCodec / dynamicJSONCodec) that bind a dynamicpb.Message to the expected response descriptor at Unmarshal time — connect-go's default codec would otherwise produce a descriptor-less empty message.
  • New transport_connect_test.go: covers Invoke over both wire formats, header passthrough (including binary), error mapping (*connect.Error is preserved through %w wrapping so callers can errors.As), context cancellation, base-URL trailing-slash handling, and the HTTPClient: nil default-fallback path.
  • New grpc_datasource_connect_test.go: end-to-end tests that mirror the existing Test_DataSource_Load_WithMockService happy path but route the call through NewConnectTransport against an httptest/H2C server backed by MockServiceConnect. One test per wire format. Proves the full pipeline (compiler → JSON builder → transport → response unmarshal) is transport-agnostic.

pkg/engine/datasource/grpc_datasource/transport.go — contract docs

The RPCTransport interface now documents the methodFullName format (/package.Service/Method, leading slash required) and the input/output expectations, so both implementations agree on the shape.

Backward compatibility

No changes to existing APIs. NewDataSource(transport RPCTransport, config DataSourceConfig) continues to accept any RPCTransport; this PR just adds a new implementation. Existing gRPC call sites are untouched.

Testing

  • pkg/engine/datasource/grpc_datasource: full test suite passes (459 cases, including 11 new Connect cases — TestConnectTransport_* and Test_DataSource_Load_WithMockServiceConnect[_JSON]).
  • pkg/engine/datasource/graphql_datasource: 1842 cases pass (no regression).
  • pkg/grpctest: builds clean; fixtures regenerated end-to-end via buf generate.

Dependencies

  • Added: connectrpc.com/connect v1.19.2
  • golang.org/x/net promoted from indirect to direct (the e2e test uses http2/h2c to run the Connect server over a real HTTP/2 endpoint).

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/docs-website — N/A: no public-facing API change worth surfacing in docs at this stage; user-facing docs land with the consuming cosmo PR.
  • I have read the Contributors Guide.

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@coderabbitai summary

Replace the hand-rolled protoc invocation with a buf-driven pipeline that
emits both gRPC and ConnectRPC stubs from product.proto. The new
productv1connect package exposes the Connect handler/client interfaces
alongside the existing gRPC code, and MockServiceConnect wires the
existing MockService into the Connect handler so the same backing
implementation can serve gRPC, gRPC-Web, and Connect from one HTTP
endpoint. This is the fixture the follow-up Connect transport tests
exercise.

MockServiceConnect is intentionally hand-maintained rather than
generated; it is a pure passthrough that only needs touch-ups when
MockService method signatures change.
Implement RPCTransport on top of connect-go so the data source can dial
ConnectRPC subgraphs without changing the planner, compiler, or JSON
builder. NewConnectTransport accepts a base URL plus a wire format
(ConnectEncodingProtobuf "proto" or ConnectEncodingJSON "json"), an
optional HTTP client, and a slice of connect.Interceptor for tracing,
logging, retries, or auth header injection.

Each procedure is served by a cached connect.Client built around a
custom codec that binds dynamicpb.Message to the expected response
descriptor at unmarshal time; connect-go's default proto codec cannot
do this because it would allocate a descriptor-less *dynamicpb.Message.
The cache is intentionally unbounded because the set of procedures is
bounded by the data source schema.

Outgoing grpc/metadata is copied onto the Connect request headers using
the same conventions as the gRPC transport, with binary headers (the
"-bin" suffix) base64-encoded per the Connect spec. Errors are wrapped
with %w so callers can still errors.As the underlying *connect.Error
to inspect Code, Message, Details, and Metadata. The response is merged
into a reset output message to avoid accumulating repeated-field values
across invocations.

The RPCTransport interface contract is now documented in transport.go,
covering the methodFullName format ("/package.Service/Method") and the
input/output expectations that both gRPC and Connect implementations
share.

Tests cover protobuf and JSON wire formats, header passthrough
(including binary), error mapping, context cancellation, trailing-slash
BaseURL handling, and the HTTPClient: nil default-fallback path.

Dependencies: adds connectrpc.com/connect v1.19.2 and promotes
golang.org/x/net from indirect to direct.
Mirror the existing MockService end-to-end happy path, but route the
call through NewConnectTransport against an httptest server backed by
the buf-generated MockServiceConnect handler. Two tests cover the
protobuf and JSON wire formats; both prove that the full data source
pipeline (compiler -> JSON builder -> transport -> response unmarshal)
behaves identically over Connect, using the same fixture, schema, and
mapping as the gRPC path.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0fc385b0-78ae-4e4a-8161-490ab570bf56

📥 Commits

Reviewing files that changed from the base of the PR and between 3b92945 and b2bf327.

📒 Files selected for processing (1)
  • v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go

📝 Walkthrough

Walkthrough

This PR adds a Connect-based gRPC transport for GraphQL datasources, configures Buf code generation for Connect/gRPC/proto, provides a Connect adapter for the mock service, and includes unit and end-to-end tests for Protobuf and JSON wire encodings.

Changes

Connect gRPC Transport Implementation

Layer / File(s) Summary
Build tooling and dependency setup
v2/go.mod, v2/pkg/grpctest/Makefile, v2/pkg/grpctest/buf.yaml, v2/pkg/grpctest/buf.gen.yaml
Adds connectrpc.com/connect v1.19.2 and promotes golang.org/x/net to direct; switches Makefile to buf generate and adds Buf v2 generation config for Go/gRPC/Connect codegen.
Test infrastructure and interface documentation
v2/pkg/engine/datasource/grpc_datasource/transport.go, v2/pkg/grpctest/mockservice_connect.go
Clarifies RPCTransport docstring and adds MockServiceConnect, an adapter that implements productv1connect.ProductServiceHandler by forwarding Connect handler calls to the existing gRPC MockService.
Connect transport implementation
v2/pkg/engine/datasource/grpc_datasource/transport_connect.go
Implements ConnectTransport, ConnectEncoding, ConnectTransportConfig, dynamic proto/json codecs that bind response descriptors, per-procedure client caching with a 10MB cap, header forwarding (including base64 for -bin), and unary Invoke with error wrapping.
Transport unit tests
v2/pkg/engine/datasource/grpc_datasource/transport_connect_test.go
Adds tests verifying protobuf/json encoding, Connect and upstream error handling, string and binary metadata forwarding, URL joining, context cancellation behavior, and default HTTP client fallback.
End-to-end datasource tests
v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go
Adds integration tests exercising GraphQL compilation and datasource Load against an h2c test server using the new Connect transport with both Protobuf and JSON encodings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(grpc_datasource): add ConnectRPC transport implementation' directly and clearly describes the main change: adding a ConnectRPC transport implementation to the gRPC datasource.
Description check ✅ Passed The description comprehensively explains the changes including new Connect transport implementation, fixture regeneration via buf, adapter wrapping, wire format support, header translation, and comprehensive testing that validate all aspects of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go (1)

180-181: ⚡ Quick win

Use structured assertions for the JSON transport path too.

Substring checks are permissive and can pass on malformed or partially incorrect payloads. Unmarshal and assert typed fields (same as the protobuf test) to keep both e2e paths equally strict.

Suggested fix
-	require.Contains(t, string(output), `"id":"test-id-123"`)
-	require.Contains(t, string(output), `"name":"Test Product"`)
+	type response struct {
+		Data struct {
+			ComplexFilterType []struct {
+				Id   string `json:"id"`
+				Name string `json:"name"`
+			} `json:"complexFilterType"`
+		} `json:"data"`
+	}
+	var resp response
+	require.NoError(t, json.Unmarshal(output, &resp))
+	require.NotEmpty(t, resp.Data.ComplexFilterType)
+	require.Equal(t, "test-id-123", resp.Data.ComplexFilterType[0].Id)
+	require.Equal(t, "Test Product", resp.Data.ComplexFilterType[0].Name)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go`
around lines 180 - 181, Replace the permissive substring checks on the JSON
transport output with structured JSON assertions: unmarshal the output bytes
(variable output) into a typed struct or map (mirroring the protobuf test's
fields), then use require.Equal/require.NotNil to assert the id equals
"test-id-123" and the name equals "Test Product" (same typed fields used in the
protobuf test) so the JSON transport path is validated strictly rather than via
contains checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go`:
- Around line 116-117: The test currently indexes resp.Data.ComplexFilterType[0]
directly which can panic when the slice is empty; update the test in
grpc_datasource_connect_test.go to first assert the slice is non-empty (e.g.,
use require.NotEmpty or require.Greater(t, len(resp.Data.ComplexFilterType), 0)
on resp.Data.ComplexFilterType) and only then assert the element fields (Id and
Name) from resp.Data.ComplexFilterType[0], so failures report assertion errors
instead of panics.

---

Nitpick comments:
In `@v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go`:
- Around line 180-181: Replace the permissive substring checks on the JSON
transport output with structured JSON assertions: unmarshal the output bytes
(variable output) into a typed struct or map (mirroring the protobuf test's
fields), then use require.Equal/require.NotNil to assert the id equals
"test-id-123" and the name equals "Test Product" (same typed fields used in the
protobuf test) so the JSON transport path is validated strictly rather than via
contains checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 181c5c95-0f96-4e7d-96bb-9286bb23d180

📥 Commits

Reviewing files that changed from the base of the PR and between 15a8132 and 3b92945.

⛔ Files ignored due to path filters (1)
  • v2/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • v2/go.mod
  • v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_connect_test.go
  • v2/pkg/engine/datasource/grpc_datasource/transport.go
  • v2/pkg/engine/datasource/grpc_datasource/transport_connect.go
  • v2/pkg/engine/datasource/grpc_datasource/transport_connect_test.go
  • v2/pkg/grpctest/Makefile
  • v2/pkg/grpctest/buf.gen.yaml
  • v2/pkg/grpctest/buf.yaml
  • v2/pkg/grpctest/mockservice_connect.go
  • v2/pkg/grpctest/productv1/productv1connect/product.connect.go

Assert resp.Data.ComplexFilterType is non-empty before indexing into it.
A regression that returns an empty slice would otherwise surface as an
opaque index-out-of-range panic instead of a clear assertion failure.

Addresses coderabbitai review feedback on wundergraph#1509.
@fengyuwusong
Copy link
Copy Markdown
Contributor Author

Hi @Noroth , Could you please take a look? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant