Skip to content

Service Communication

Use createClient() + createGrpcTransport() from ConnectRPC to call other services. Add createOtelClientInterceptor() for distributed tracing and createDefaultInterceptors() with client-safe options for resilience.

Quick Start

typescript
import { createClient } from '@connectrpc/connect';
import { createGrpcTransport } from '@connectrpc/connect-node';
import { createOtelClientInterceptor } from '@connectum/otel';
import { createDefaultInterceptors } from '@connectum/interceptors';
import { InventoryService } from '#gen/inventory/v1/inventory_pb.js';

// 1. Create transport with OTel + resilience interceptors
const inventoryTransport = createGrpcTransport({
  baseUrl: `http://${process.env.INVENTORY_HOST}:${process.env.INVENTORY_PORT}`,
  httpVersion: '2',
  interceptors: [
    createOtelClientInterceptor({
      serverAddress: process.env.INVENTORY_HOST!,
      serverPort: Number(process.env.INVENTORY_PORT),
    }),
    ...createDefaultInterceptors({
      circuitBreaker: { failureThreshold: 5 },
      timeout: { duration: 5_000 },
      retry: { maxRetries: 2 },
      // Disable server-only interceptors
      bulkhead: false,
      errorHandler: false,
      serializer: false,
      validation: false,
    }),
  ],
});

// 2. Create typed client
const inventoryClient = createClient(InventoryService, inventoryTransport);

// 3. Call from any handler
const stock = await inventoryClient.checkStock({ sku: 'ABC-123' });

Trace context propagates automatically -- the client span links to the server span in the downstream service.

Key Concepts

Transport Configuration

createGrpcTransport() creates an HTTP/2 transport for gRPC communication:

OptionTypeDescription
baseUrlstringTarget service URL (e.g. http://order-service:5000)
httpVersion'2'HTTP version (always '2' for gRPC)
interceptorsInterceptor[]Client-side interceptor chain

For ConnectRPC protocol (HTTP/1.1 JSON), use createConnectTransport() instead.

Client vs Server Interceptors

Not all server interceptors are appropriate for client transports. When using createDefaultInterceptors() on the client side, disable server-only interceptors:

InterceptorServerClientNotes
errorHandlerYesNoNormalizes errors for responses -- not needed on client
timeoutYesYesEnforce per-request deadline
bulkheadYesNoLimits server concurrency -- not applicable to clients
circuitBreakerYesYesPrevents cascading failures to downstream services
retryYesYesRetries transient errors
fallbackYesNoRequires server-side handler
validationYesNoValidates incoming requests -- not outgoing
serializerYesNoServer-side JSON serialization

Service Discovery

PatternWhen to UseExample
Kubernetes DNSMost K8s deploymentshttp://order-service.production.svc.cluster.local:5000
Environment variablesSimple setups, Docker Composehttp://${process.env.ORDER_HOST}:${process.env.ORDER_PORT}
Buf Schema RegistryPolyrepo proto managementCentralized proto definitions via BSR
gRPC Server ReflectionDevelopment, toolingRuntime API discovery via grpcurl, grpcui

In Kubernetes, services are discoverable via DNS. A Connectum service deployed as order-service in namespace production is reachable at order-service.production.svc.cluster.local:5000. No external service registry is needed.

Learn More