Skip to content
Kavachकवच

gRPC

Also known as: rpc protobuf protocol buffers microservice proto service stub remote call streaming bidirectional bidi client stream server stream chat conversation duplex realtime

● Intermediate5 minBefore this: HTTP, RecordsSpec 14.14

gRPC services from your own Kavach types: no .proto file to write first, no code generator, and any gRPC library can call them.

use std.grpc
use std.net

type Order {
    id: Int
    item: String
    qty: Int = 1
}

type Placed {
    id: Int
    total: Float
}

fn orders(call: GrpcCall) -> GrpcReply {
    const order = grpc.read<Order>(call) catch _ => Order(id: 0, item: "")
    if order.id == 0 {
        return grpc.failed(3, "not an order")
    }
    grpc.reply(Placed(id: order.id, total: 2.5 * Float(order.qty)))
}

fn main() ! Error {
    const listener = try net.listen("127.0.0.1:0")
    const _server = spawn grpc.serve(listener, orders)
    const target = "http://{listener.address()}"
    const placed = try grpc.call<Order, Placed>(target, "/shop.Orders/Place", Order(id: 7, item: "pen", qty: 2))
    print(placed.total)
    const bad = grpc.call<Order, Placed>(target, "/shop.Orders/Place", Order(id: 0, item: "")) catch e => {
        print("failed: {e}")
        return
    }
    print(bad.total)
}

The program ran and printed:

kavach run call.kav

5.0failed: Status(code: 3, message: "not an order")
call.kav: Kavach code, then what running it prints
  • The handler reads the request into an Order and answers with a Placed. The types are the messages.
  • grpc.failed(3, …) answers with a gRPC status; 3 is INVALID_ARGUMENT. The client gets it as Status(code, message).
  • http:// means HTTP/2 without encryption, as inside a company's network; https:// uses TLS.

Every function

FunctionGives
grpc.call<Req, Res>(target, method, request) -> Res ! GrpcErrorCalls method (/shop.Orders/Place) on the server at target with request, and decodes its answer into a Res
grpc.call_stream<Req, Res>(target, method, requests) -> Res ! GrpcErrorClient streaming: sends each of requests as its own message on one call, and decodes the one answer into a Res
grpc.each<Req, Res>(target, method, request, on) ! GrpcErrorServer streaming: runs on: fn(Res) for each message of the answer as it arrives
grpc.each_stream<Req, Res>(target, method, requests, on) ! GrpcErrorTwo-way streaming: sends each of requests, then runs on for each message of the answer as it arrives
grpc.serve(listener, handler) ! NetErrorServes gRPC on listener: each call is answered by handler: fn(GrpcCall) -> GrpcReply, each connection in a task of its own
grpc.serve_talks(listener, handler, talks) ! NetErrorServes like serve, but holds a conversation on each method in talks: [String]: handler answers each of its messages as soon as it arrives
grpc.talk_with<Req, Res>(target, method, first, on) ! GrpcErrorA conversation: sends first, then runs on: fn(Res) -> [Req]? for each answer as it arrives, sending what it gives; none ends the client's side
grpc.read<T>(call) -> T ! GrpcErrorA call's message decoded into a T (the first, when the client sent several)
grpc.read_all<T>(call) -> [T] ! GrpcErrorEvery message the client sent on the call, in order, each decoded into a T
grpc.reply<T>(value) -> GrpcReplyAn answer with value
grpc.reply_stream<T>(values) -> GrpcReplyAn answer that streams values, one message each
grpc.failed(code, message) -> GrpcReplyAn answer with a gRPC status other than OK (3 is INVALID_ARGUMENT, 5 NOT_FOUND, …)
grpc.encode<T>(value) -> Bytesvalue as a Protocol Buffers message
grpc.decode<T>(data) -> T ! GrpcErrorA Protocol Buffers message decoded into a T

One page for each form

Go deeper the why, for readers who want it

Messages are Protocol Buffers worked out from each type: field n of a record is its nth declared field. Calls are HTTP/2 POSTs with the status in a trailer, as the gRPC standard says, so a Go, Java or Python service can call a Kavach one, and the other way round.