grpc.cr

Test Lines of Code

gRPC client/server for Crystal >= 1.19.1, built on libnghttp2 and proto.cr. Supports all four RPC shapes, live full-duplex streaming, TLS, metadata, deadlines, interceptors, health and reflection.

Installation

Install libnghttp2, add the dependency, then run shards install:

sudo apt-get install libnghttp2-dev  # Debian/Ubuntu
# macOS:
brew install libnghttp2
export LIBRARY_PATH="$(brew --prefix libnghttp2)/lib${LIBRARY_PATH:+:$LIBRARY_PATH}"
dependencies:
  grpc:
    github: kojix2/grpc

Try it

From this checkout, run the server and client in separate terminals:

crystal run examples/helloworld/server.cr
crystal run examples/helloworld/client.cr -- localhost 50051 Alice

See runnable examples for streaming, TLS and interceptors.

Generate your API

From this checkout, build both plugins; protoc must also be installed:

mkdir -p bin
crystal build lib/proto/src/protoc-gen-crystal_main.cr -o bin/protoc-gen-crystal
shards build
protoc --plugin=protoc-gen-crystal=bin/protoc-gen-crystal \
  --plugin=protoc-gen-crystal-grpc=bin/protoc-gen-crystal-grpc \
  --crystal_out=. --crystal-grpc_out=. path/to/service.proto

Replace the schema path with your own. In the consuming application, require grpc first, then the generated .pb.cr and .grpc.cr files. Unary methods return the decoded message or raise GRPC::StatusError; the corresponding *_response method returns status and metadata. Consume or cancel streams. If an RPC already uses a helper's name, the generator adds a numeric suffix to the helper (for example foo_response_2). Message types and type_map targets are emitted as absolute Crystal names.

Live bidi calls expose final_status (nil until RPC completion, independent of reply consumption). SendClosedError means the message was not enqueued; its final_status is nil for a request half-close. Drain remaining replies on completion.

After GOAWAY, new RPCs use a replacement connection while accepted RPCs finish on the old connection. Calls are not automatically retried.

TLS configures h2 ALPN on supplied client and server contexts as well as default contexts. The client requires the peer to negotiate h2.

Connection attempts are shared between callers; each ClientContext can stop waiting at its own deadline or through ctx.cancel. The default connection timeout is 10 seconds; EndpointConfig#connect_timeout also bounds the TLS handshake. Failed attempts use BackoffConfig before the next attempt. A failed RPC is never automatically replayed. The default HTTP/2 transport also propagates context cancellation to active RPCs; custom transports must implement the context-aware overloads to provide the same behavior.

HTTP/2 PING keepalive is disabled unless EndpointConfig#keepalive is set. Configure its interval with the service owner: overly frequent PINGs can cause the peer to send GOAWAY. TCP keepalive and HTTP/2 PING are separate mechanisms. When the Crystal socket API supports setting the TCP keepalive idle time, EndpointConfig#tcp_keepalive also configures that time.

Receive limits

GRPC::ReceiveLimits defaults to 64 MiB each for wire payload, decoded message and unconsumed decoded data per stream. Set it through EndpointConfig.new(receive_limits: limits) on clients or Server.new(receive_limits: limits) on default servers. Excess data ends that RPC with RESOURCE_EXHAUSTED; these are not process-wide RAM limits. Custom transports enforce their own budgets.

Regenerate existing .grpc.cr clients to use on-demand typed response decoding. Responses are decoded when iterated, so unread data stays in the transport's byte budget.

Clients can send gzip requests with ClientContext.new(compression: "gzip") and decode gzip responses. Servers decode gzip requests. Set Server.new(response_compression: true) to compress responses when the client advertises gzip. Reflection serves both v1 and v1alpha.

Send limits

GRPC::SendLimits defaults to 64 MiB per message, 64 MiB plus the frame header per streaming queue, and 256 MiB across streaming queues on one connection. Set it through EndpointConfig.new(send_limits: limits) on clients, ClientContext.new(send_limits: limits) for a client stream override, or Server.new(send_limits: limits) on default servers. The connection-wide limit comes from the endpoint, not the per-call override. Oversized messages raise RESOURCE_EXHAUSTED; a full queue waits for capacity, deadline or cancellation. These limits cover retained streaming frames, not unary bodies, temporary encoding buffers, application-owned data or process-wide RAM. The socket writer separately buffers approximately 256 KiB of serialized HTTP/2 output, plus at most one nghttp2 output chunk, per connection.

With an updated proto.cr runtime and regenerated message and gRPC stubs, set ClientContext.new(protobuf_options: GRPC::ProtobufOptions.new(...)) and Server.new(protobuf_options: ...) to configure the Protobuf limits. A custom marshaller_for can also return ProtoMarshaller(T).new with encode_options: and decode_options:. Existing generated types continue to work with the default marshaller; requesting options with an old message type raises an argument error. Protobuf limits are independent of gRPC frame limits.

Test

crystal spec
crystal spec --order 20261102  # replay a shuffled CI run
crystal spec spec/grpcurl.cr  # requires grpcurl

CI checks the minimum supported Crystal version and latest Crystal, runs seeded fragmentation campaigns, regenerates and checks protoc fixtures, and tests the client against locked and latest grpc-go dependencies over plaintext and TLS. A separate job regenerates and tests the current MinKNOW client against the candidate gRPC source. Nightly runs expand fragmentation coverage.

See test layout for focused tests and GripMock setup. The committed shard.lock pins proto.cr for development, code generation and CI; install it with shards install --frozen. Applications resolve their own lock, so update grpc and proto together when adopting regenerated messages. License: MIT.

Repository

grpc

Owner
Statistic
  • 5
  • 0
  • 1
  • 3
  • 1
  • about 1 hour ago
  • April 15, 2026
License

MIT License

Links
Synced at

Tue, 10 Nov 2026 12:37:46 GMT

Languages