Skip to content

Connect RPC

wireform-connect is a native Haskell implementation of the Connect RPC protocol: a client and server for unary and all three streaming RPC kinds, over both HTTP/1.1 and HTTP/2, with binary Protobuf and JSON codecs, GET for side-effect-free unary calls, identity/gzip/br/zstd compression, the gRPC-derived error-code model, leading/trailing metadata, and the streaming EndStreamResponse envelope.

Connect speaks the same Protobuf services as gRPC but over plain HTTP: no HTTP/2 requirement, no custom framing on unary calls, and responses any HTTP client (curl, a browser, fetch) can read directly. Reach for wireform-connect when you want a gRPC-style service that is also callable from plain HTTP; reach for wireform-grpc when you need gRPC’s wire compatibility specifically.

The headline design point: Connect is purely a new transport over the existing service description. The Protobuf serv "meth" service-method tags that loadProtoServices emits for gRPC, together with the message types from loadProto, drive Connect unchanged. There is no Connect-specific code generator — you write one .proto, run the same two TH splices, and the generated service works under gRPC and Connect. Connect ignores the application/grpc+proto content-type baked into each tag and computes its own (application/proto, application/json, application/connect+proto, application/connect+json) from the codec and streaming kind.

The proto3-JSON path works because the Proto newtype carries ToJSON / FromJSON instances (defined in grpc-spec), delegating to each message’s generated aeson instances — so a method’s Input and Output serialize in either codec with no unwrapping.

Generate the service tags and message types once (the module needs {-# LANGUAGE DataKinds #-}):

{-# LANGUAGE DataKinds, TemplateHaskell, FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
module Eliza where
import Network.GRPC.Protobuf.TH (loadProtoServices)
import Proto.TH (loadProto)
$(loadProto "proto/eliza.proto")
$(loadProtoServices "proto/eliza.proto")

A server — one method per RPC (the handler shape is inferred from the method’s streaming kind), bundled into a completeness-checked Service:

import Network.Connect.Server
import Network.HTTP.Server (defaultServerConfig, ServerConfig (..))
import Network.HTTP.VersionRange (preferHttp20)
import Network.GRPC.Spec (Proto (..))
import Eliza
main :: IO ()
main = runConnectServer defaultConnectServerConfig serverCfg (connectHandlers eliza)
where
serverCfg = defaultServerConfig
{ serverPort = "8080", serverVersionRange = preferHttp20 }
eliza :: Service ElizaService ConnectServerM
eliza =
service
( method @Say say
:& method @Introduce introduce
:& method @Converse converse
:& Done
)
where
say (Proto req) = pure (Proto defaultSayResponse
{ sayResponseSentence = "Hello, " <> sayRequestSentence req })

A client:

import Network.Connect.Client
import Network.HTTP.Client
(defaultConnectionConfig, ConnectionConfig (..))
import Network.GRPC.Spec (Proto (..))
import Data.Proxy (Proxy (..))
import Eliza
main :: IO ()
main = do
let connCfg = defaultConnectionConfig
{ connectionHost = "localhost", connectionPort = "8080" }
withConnectClient defaultConnectClientConfig connCfg $ \cl -> do
Proto resp <- nonStreaming cl (Proxy @Say)
(Proto defaultSayRequest { sayRequestSentence = "Hi" })
print (sayResponseSentence resp)

For the full, runnable walkthrough — dependencies, codegen, running the server, calling it, and the demo.connectrpc.com interop check — see Getting started. For the per-method API, see Serving Connect RPCs and Calling Connect RPCs; for the on-the-wire shapes (content types, the streaming envelope, unary GET, compression), see Wire protocol.

ModuleRole
Network.ConnectUmbrella re-export of the public surface
Network.Connect.ServerrunConnectServer, connectHandlers + the service / method registration vocabulary (shared with wireform-grpc), ConnectServerM + metadata accessors
Network.Connect.ClientwithConnectClient + nonStreaming / nonStreamingGet / serverStreaming / clientStreaming / biDiStreaming
Network.Connect.ProtocolCodecs, the content-type matrix, reserved header names, GET query parameters
Network.Connect.ErrorConnectError / ConnectException, the code↔name and code↔HTTP-status tables, the JSON error envelope
Network.Connect.EnvelopeThe streaming frame (1 flag byte + 4-byte length) + EndStreamResponse
Network.Connect.MetadataCustomMetadata ↔ HTTP headers (ASCII / -bin base64 / trailer- prefix)
Network.Connect.Codecproto / JSON message-body (de)serialization
Network.Connect.Compressionidentity / gzip / br / zstd + accept-encoding negotiation

Like wireform-grpc, this is an RPC framework: it owns the Network.Connect.* namespace (not wireform’s per-format <Format>.* convention) and is not re-exported through the umbrella wireform package.