@samveys/grpc

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/grpc

From packages/grpc/README.md in the framework repository.

gRPC on Connect for @samveys/http. One service definition is served over three protocols. Runtime dependencies: @connectrpc/connect, @connectrpc/connect-node, @bufbuild/protobuf.

ProtocolWhereNeeds
Connect (JSON or binary, plain POST)The app's HTTP portnothing
gRPC-WebThe app's HTTP portnothing
gRPCA separate HTTP/2 (h2c) port, http2Porthttp2Port

Native gRPC needs HTTP trailers, which the Fetch-based kernel cannot send, so it gets its own HTTP/2 listener. Connect and gRPC-Web are answered ahead of routing through the http:request intercept, so they use the app's port, timeouts and drain. The extra listener closes at shutdown.

Set up

ts
import { grpc, GreetService } from '@samveys/grpc'
import { http } from '@samveys/http'

export const plugins = [
  http(),
  grpc({
    http2Port: 50051,
    routes: (router) =>
      router.service(GreetService, {
        greet: (request) => ({ greeting: `Hello, ${request.name}!` }),
      }),
  }),
]

GreetService (samveys.example.v1.GreetService/Greet) is a sample shipped with the package. Your own services come from protoc-gen-es output, or are built without codegen from an embedded descriptor (see src/example.ts).

Calling it with curl

The Connect protocol is plain HTTP with a JSON body:

ts
// curl -H 'Content-Type: application/json' -d '{"name":"Ann"}' \
//   http://127.0.0.1:3333/samveys.example.v1.GreetService/Greet
// → {"greeting":"Hello, Ann!"}

Calling it from code

ts
import { createClient } from '@connectrpc/connect'
import { createConnectTransport, createGrpcTransport } from '@connectrpc/connect-node'
import { GreetService } from '@samveys/grpc'

// Connect protocol on the app's port.
const connect = createClient(GreetService, createConnectTransport({ baseUrl: 'http://127.0.0.1:3333', httpVersion: '1.1' }))

// Native gRPC on the HTTP/2 port.
const native = createClient(GreetService, createGrpcTransport({ baseUrl: 'http://127.0.0.1:50051' }))

export const greetings = [await connect.greet({ name: 'Ann' }), await native.greet({ name: 'Bo' })]

Port layout

http2Port: 0 picks a free port; read it with container.make('grpc').http2Address(). http2Host defaults to 127.0.0.1. Leave http2Port out to serve only Connect and gRPC-Web. connect: false or grpcWeb: false turns one of those off. The remaining options are Connect router options (interceptors, readMaxBytes, maxTimeoutMs, …).