Skip to content
TypeWire

@tahanabavi/typefetch-grpc

documents v0.0.0

gRPC transport for TypeFetch contracts — Connect unary JSON out of the box, binary grpc-web behind a codec seam, zero dependencies.

Errors

Failures normalise onto typefetch's shared ErrorKind, so one global handler covers every transport:

ts
client.onError((error) => {
if (error.kind === "unauthenticated") redirectToLogin();
});

That fires for a gRPC UNAUTHENTICATED exactly as it does for an HTTP 401 and a GraphQL UNAUTHENTICATED.

The typed error body stays gRPC-native: a gRPC endpoint's errors map is keyed by the numeric gRPC code, not the HTTP status.

ts
import { GrpcCode } from "@tahanabavi/typefetch-grpc";

getUser: {
transport: "grpc",
service: "user.v1.UserService",
rpc: "GetUser",
request: z.object({ id: z.string() }),
response: User,
errors: {
[GrpcCode.NotFound]: z.object({ code: z.string(), message: z.string() }),
},
}
ts
if (isContractError(contracts.user.getUser, e, GrpcCode.NotFound)) {
e.data.message; // typed from the schema above
}

Keying on the code rather than the status is what makes a contract's declared errors mean the same thing over Connect (which maps codes onto real statuses) and over binary grpc-web (which answers 200 and reports the code in a trailer). RichError.grpcCode carries the numeric code; RichError.status stays the HTTP status.