Skip to content

@forinda/kickjs-grpc ​

Connect RPC transport for KickJS — serves gRPC, gRPC-Web, and the Connect protocol from the same app and port as your HTTP routes, using the same decorator-driven approach as HTTP controllers.

See the gRPC guide for the full walkthrough.

Installation ​

pnpm add @forinda/kickjs-grpc @connectrpc/connect @connectrpc/connect-node @bufbuild/protobuf

@connectrpc/connect, @connectrpc/connect-node, and @bufbuild/protobuf are peer dependencies — they carry the protocol implementation and the generated descriptor runtime.

Quick example ​

ts
import { bootstrap, Autowired } from '@forinda/kickjs'
import { GrpcAdapter, GrpcService, GrpcMethod, type GrpcContext } from '@forinda/kickjs-grpc'
import { UserService, type GetUserRequest } from './gen/user_pb'
import { UserModule, UserRepository } from './modules/users' // your app's module + repository

@GrpcService(UserService)
export class UserRpc {
  @Autowired() private users!: UserRepository

  @GrpcMethod()
  async getUser(req: GetUserRequest, ctx: GrpcContext) {
    return { user: await this.users.findById(req.id) }
  }
}

bootstrap({ modules: [UserModule], adapters: [GrpcAdapter({ prefix: '/rpc' })] })

Exports ​

Decorators ​

DecoratorDescription
@GrpcService(descriptor)Bind a DI-registered class to a generated protobuf DescService
@GrpcMethod(rpc?)Bind a class method to an RPC (defaults to the method's own name)
ts
function GrpcService(service: DescService): ClassDecorator // also registers the class as a @Service()
function GrpcMethod(rpc?: string): MethodDecorator

RPCs declared in the proto but not bound to a method answer Code.Unimplemented.

Adapter ​

ExportDescription
GrpcAdapterAppAdapter that mounts Connect RPC on the existing HTTP server
GRPC_ADAPTERDI token resolving to { getStats(), listServices() }

Context ​

ExportDescription
GrpcContextPer-RPC context; implements the transport-neutral ExecutionContext
MemberDescription
service / methodFully-qualified service name and RPC name from the proto
protocolconnect, grpc or grpc-web
urlURL the server received
headers / header(name)Incoming metadata as Headers, or one entry case-insensitively
responseHeaderOutgoing headers (set before the first streamed message)
responseTrailerOutgoing trailers
signalAborts on deadline, client cancel, or completion
deadlineMsMilliseconds left before the caller's deadline, or undefined
requestIdIncoming x-request-id, else a UUID generated once per call
get(key) / set(key, v)Typed per-RPC values written by Context Contributors
require(key)Like get, but throws MissingContextValueError when the value is undefined
handlerThe raw Connect HandlerContext (escape hatch)

Errors ​

ExportDescription
toConnectError(err)Normalize any thrown value into a ConnectError. Unexpected errors are redacted to INTERNAL_ERROR_MESSAGE; the original stays on cause
INTERNAL_ERROR_MESSAGEThe opaque message sent for every Code.Internal response ("Internal error")
codeForStatus(n)Map an HTTP status onto the corresponding Connect Code (unmapped 5xx → Code.Internal)

A thrown HttpException is mapped through codeForStatus, so a 404 reaches the client as Code.NotFound, with its message and headers kept. Other errors go out as Code.Internal with INTERNAL_ERROR_MESSAGE; the original stays on cause.

Client ​

ExportDescription
propagateCallConnect Interceptor for outgoing calls: forwards x-request-id and traceparent, and inside an RPC cancels the call when the incoming one ends
GRPC_CALLRequest-store key holding the GrpcContext of the RPC being served

Lower-level ​

ExportDescription
collectServices()Read decorated service classes out of the registry
buildConnectRoutes(opts)Turn service entries into a Connect routes function
grpcServiceRegistryThe Set of @GrpcService-decorated classes
GRPC_METADATAMetadata keys this package writes (SERVICE, HANDLERS)

Types ​

ExportDescription
GrpcAdapterOptionsOptions accepted by GrpcAdapter
GrpcAdapterExtensionsPublic surface behind the GRPC_ADAPTER token
GrpcServiceEntryA service class paired with its descriptor
GrpcHandlerDefinitionOne @GrpcMethod binding
GrpcStatsSnapshot returned by getStats()
BuildConnectRoutesOptionsInput to buildConnectRoutes
GrpcNodeRequestNode IncomingMessage, as Connect sees it

GrpcStats is { services, methods, callsTotal, callsFailed, byMethod: Record<string, { calls, failed }> }.

Adapter options ​

OptionDefaultDescription
prefix''Path prefix for all RPCs. Leave empty for native gRPC clients.
grpctrueEnable the native gRPC protocol (needs HTTP/2 — see below)
grpcWebtrueEnable gRPC-Web (works over HTTP/1.1)
connecttrueEnable the Connect protocol (JSON over HTTP, curl-able)
contributors[]Context Contributors applied to every RPC, at the adapter level of the method > class > module > adapter > global chain. module and global are HTTP-only and never reach an RPC — this is the way to cover all of them.
routes—(router: ConnectRouter) => void — register services Connect-style; runs after decorated services, so it can override them
readMaxBytes—Max size of an inbound message
writeMaxBytes—Max size of an outbound message
onError—(err, { service, method }) => unknown — called for errors that aren't ConnectError / HttpException; return a ConnectError or undefined (→ Code.Internal)

Protocol support ​

The adapter rides the KickJS server:

ProtocolHTTP/1.1 (default)server: { tls, http2: true }Notes
Connect✅✅JSON or binary over HTTP
gRPC-Web✅✅Browser clients
native gRPC❌✅ (Fastify, h3)Needs HTTP/2 + trailers on every hop

Behind a service mesh that speaks cleartext HTTP/2 to the app, server: { http2: 'h2c' } serves native gRPC without a certificate.

Released under the MIT License. Built with TypeScript — runs on Express, Fastify, or h3.