yatta/grpcweb
gRPC-Web unary calls: protobuf codec, framing, handler, client.
38 exported symbols and 18 members, read from src/func/grpcweb.ts.
Construct
concat
functionJoin byte arrays in order. The result is a copy, so it is safe to keep.
concat(...parts: Uint8Array[]): Uint8ArraycreateGrpcWebHandler
functionBuild the HTTP handler for gRPC-Web unary calls.
Mount it on `options.basePath` and give it a service map. Anything that is not a `POST` to `<basePath>/<Service>/<Method>` is answered before a service is consulted.
createGrpcWebHandler(services?: GrpcServices, options?: GrpcWebOptions): (req: Request) => Promise<Response>- services
- The methods to expose. Defaults to the health check and an echo, which are useful for a smoke test and harmless otherwise.
Returns A `(req) => Promise<Response>` to hand to a router or `Bun.serve`.
decodeFrames
functionSplit a gRPC-Web body into its frames.
decodeFrames(data: Uint8Array, maxFrameBytes?: ): GrpcFrame[]- maxFrameBytes
- Refuse a frame above this size before reading it. The length comes from the caller, so a 5-byte header can otherwise announce four gigabytes and have the server try to find them.
decodeMessage
functionSplit one encoded message into its fields, in wire order.
Every length is checked against the bytes actually present before it is used, so a truncated message is an error rather than a short read.
decodeMessage(data: Uint8Array): ProtoField[]decodeVarint
functionRead one varint that starts at `offset`.
decodeVarint(data: Uint8Array, offset: number): { value: bigint; next: number; }Returns `value`, and `next` — the offset just past the varint, so a caller can keep walking a message.
decodeZigZag
functionUndo .
decodeZigZag(n: bigint): bigintencodeBooleanField
functionWire type 0 with 1 or 0. Protobuf has no boolean on the wire.
encodeBooleanField(field: number, value: boolean): Uint8ArrayencodeBytesField
functionWire type 2: tag, length, then the bytes untouched.
encodeBytesField(field: number, body: Uint8Array): Uint8ArrayencodeDoubleField
functionWire type 1: eight bytes, little-endian.
encodeDoubleField(field: number, value: number): Uint8ArrayencodeFloatField
functionWire type 5: four bytes, little-endian, as protobuf requires.
encodeFloatField(field: number, value: number): Uint8ArrayencodeFrame
functionFrame a payload: one flag byte, a 4-byte big-endian length, then the bytes.
encodeFrame(data: Uint8Array, trailer?: ): Uint8ArrayencodeMessageField
functionWire type 2 with a message the caller has already encoded.
encodeMessageField(field: number, messageBytes: Uint8Array): Uint8ArrayencodeStringField
functionWire type 2, with the string encoded as UTF-8.
encodeStringField(field: number, value: string): Uint8ArrayencodeVarint
functionEncode an integer as a base-128 varint.
A negative value becomes its 64-bit two's complement, which is how protobuf writes `int64` — ten bytes every time. Use for `sint*` fields to avoid that.
encodeVarint(value: bigint | number): Uint8ArrayencodeVarintField
functionWire type 0: tag then varint.
encodeVarintField(field: number, value: number | bigint): Uint8ArrayencodeZigZag
functionZigZag-encode a signed value for a `sint32`/`sint64` field.
Without it a small negative number is ten bytes of sign extension; with it, -1 is one byte.
encodeZigZag(n: bigint | number): bigintfieldBoolean
functionA varint field read as a boolean: anything other than 0 is true.
fieldBoolean(fields: ProtoField[], field: number): boolean | nullfieldBytes
functionThe last length-delimited field with this number, as raw bytes.
fieldBytes(fields: ProtoField[], field: number): Uint8Array | nullReturns null when the field is absent. The bytes are a view into the decoded message, so copy them before keeping them.
fieldDouble
functionA 64-bit float field (wire type 1). Null when absent or the wrong width.
fieldDouble(fields: ProtoField[], field: number): number | nullfieldFloat
functionA 32-bit float field (wire type 5). Null when absent or the wrong width.
fieldFloat(fields: ProtoField[], field: number): number | nullfieldMessage
functionA nested message, already decoded. Null when the field is absent.
fieldMessage(fields: ProtoField[], field: number): ProtoField[] | nullfieldNumber
functionas a JavaScript number.
Loses precision past 2^53, so read ids and timestamps with instead.
fieldNumber(fields: ProtoField[], field: number): number | nullfieldRepeatedBytes
functionEvery value of a repeated bytes field, in order.
fieldRepeatedBytes(fields: ProtoField[], field: number): Uint8Array[]fieldRepeatedMessage
functionEvery value of a repeated message field, decoded.
fieldRepeatedMessage(fields: ProtoField[], field: number): ProtoField[][]fieldRepeatedString
functionEvery value of a repeated string field, in order.
fieldRepeatedString(fields: ProtoField[], field: number): string[]fieldRepeatedVarint
functionEvery value of a repeated varint field, in order.
Proto3 packs them into one length-delimited field (wire type 2) and proto2 repeats the tag (wire type 0). Both appear in real traffic, so both are read rather than assuming the sender's proto version.
fieldRepeatedVarint(fields: ProtoField[], field: number): bigint[]fieldString
functionThe last length-delimited field with this number, read as text.
fieldString(fields: ProtoField[], field: number): string | nullReturns null when the field is absent — an absent field and an empty string are different answers, and callers rely on that.
fieldVarint
functionThe last varint field with this number.
fieldVarint(fields: ProtoField[], field: number): bigint | nullReturns null when absent, so a legitimate 0 is not mistaken for "no field".
grpcWebCall
async functionCall a unary gRPC-Web method and return the raw reply message.
Works in a browser and in Bun/Node, because it goes through `fetch`. The reply is unwrapped for you: trailers are parsed and the message bytes are returned.
grpcWebCall(endpoint: string, service: string, method: string, message: Uint8Array, options?: GrpcWebCallOptions): Promise<Uint8Array>- endpoint
- Base URL, like `"https://api.example.com/grpc-web"`.
GrpcError
classAn error carrying a gRPC status code.
Throw one from a service to choose what the client sees. Anything else is logged and answered as `INTERNAL`, so an unexpected failure does not put its message on the wire.
class GrpcError extends Error2 members
codepropertycode: numbergrpcStatusgettergrpcStatus: number
Types
GrpcContext
interfaceWhat a service gets besides the request message.
interface GrpcContext2 members
reqpropertyreq: RequestThe original HTTP request: headers, cookies, and the caller's identity.
signalpropertysignal?: AbortSignal
GrpcFrame
interfaceOne frame of a gRPC-Web body.
`trailer` marks the final frame, which carries `grpc-status` as text instead of a message. `compressed` is the flag this server never sets: it negotiates no compression, and refusing the flag is clearer than silently handing back bytes nobody decompressed.
interface GrpcFrame3 members
trailerpropertytrailer: booleancompressedpropertycompressed: booleandatapropertydata: Uint8Array
GrpcWebCallOptions
interfaceinterface GrpcWebCallOptions4 members
headerspropertyheaders?: Record<string, string>Extra headers, for example `{ authorization: "Bearer …" }`.
signalpropertysignal?: AbortSignalAbort the call from the caller's side.
formatpropertyformat?: "binary" | "text"`"text"` sends base64 over `application/grpc-web-text`, which is what
timeoutMspropertytimeoutMs?: numberSent as a `grpc-timeout` header. The server is free to ignore it.
GrpcWebOptions
interfaceinterface GrpcWebOptions3 members
basePathpropertybasePath?: stringWhere the handler is mounted. Default `/grpc-web`; a trailing slash is ignored.
maxMessageBytespropertymaxMessageBytes?: numberLargest message accepted, in bytes. Default 4 MB. A reply is not
corsOriginspropertycorsOrigins?: string[]Browser origins allowed to call this endpoint, like
Unset means no CORS headers, so a browser on another origin is refused by the browser itself. An `OPTIONS` from an origin not on the list gets a 403 rather than a permissive preflight.
ProtoField
interfaceOne decoded protobuf field.
`varint` is set for wire type 0, `bytes` for 1 (fixed64), 2 (length-delimited) and 5 (fixed32). Values are `bigint`, not `number`, so a real 64-bit id survives the round trip.
interface ProtoField4 members
fieldpropertyfield: numberwireTypepropertywireType: 0 | 1 | 2 | 5varintpropertyvarint?: bigintbytespropertybytes?: Uint8Array
GrpcServiceFn
typeOne unary method: request message in, reply message out.
Return bytes, not a `Response` — framing, trailers and status codes are the handler's job. Throw to pick the status code.
type GrpcServiceFn = (request: Uint8Array, ctx: GrpcContext) => Promise<Uint8Array> | Uint8ArrayGrpcServices
typeMethods keyed `"package.Service/Method"`, the same string the client
type GrpcServices = Record<string, GrpcServiceFn>GrpcStatusCode
typeAny of the numeric values in .
type GrpcStatusCode = (typeof GrpcStatus)[keyof typeof GrpcStatus]AuthConfig or JobPayload — declaring your schema once is enough for the rest to follow. See Typed keys.