Skip to content

A nicer way to build
your gRPC server and client.

A small, opinionated framework that wraps @grpc/grpc-js and @grpc/proto-loader so you can ship a typed service or client in a handful of lines.

$npm i grpcity
import { loader } from './loader.js'

class Greeter {
  async sayGreet(call) {
    return { message: `hello, ${call.request.name}` }
  }
}

await loader.init()
const server = await loader.initServer()
server.add('helloworld.Greeter', new Greeter())
await server.listen('127.0.0.1:9099')

Built for production from day one.

Every gRPCity release ships with the features below on, behind a stable API.

Streaming, four ways

Unary, client stream, server stream, and bidi — all consumable with for await. Throwing in a handler always reaches the client as a status.

AbortSignal, end to end

Cancel any RPC by passing an AbortSignal. Pre-abort short-circuits the call; mid-flight abort cancels and surfaces as CANCELLED.

Middleware on both sides

Koa-style (ctx, next) for clients and servers — log, trace, retry, mutate metadata, all without touching the call site.

gRPC reflection built in

loader.initReflection() returns a service you can inject() straight into the server, so grpcurl and Postman just work.

Mutual TLS in three lines

Helpers (makeServerCredentials, makeClientCredentials) hide the boilerplate; full mTLS in a few function calls.

Validation that catches typos

Loader, client, and server options are validated at runtime with zod — typos fail loudly, not silently.

TypeScript, first class

Written in TypeScript end to end. Types for Metadata, credentials, ChannelCredentials, StatusObject, and more re-exported from the entry point.

Promise & callback

Use await everywhere by default; callback variants stay around for legacy integrations and event-driven hot paths.

Why gRPCity over raw @grpc/grpc-js?

gRPCity sits on top of @grpc/grpc-js — it does not replace it. The point of the wrapper is to make the parts you write every day shorter and the parts you forget about safer.

@grpc/grpc-js
gRPCity
Bootstrap a service
20+ lines of proto loading and method binding
3 lines: loader → server → listen
Cancel an in-flight RPC
Manual call.cancel(); no AbortSignal integration
Pass signal to any RPC — pre-abort and mid-call abort just work
Streaming consumption
Event emitters with manual back-pressure
for await (const m of call.readAll()) everywhere
Error handling
Error payload, no typed shape
GrpcClientError with code / details / metadata
Reflection / mTLS / middleware
Each one is a separate package or hand-rolled
Built in, on by default, behind the same loader