Code Generation
gRPCServer integrates with ProtoBuf.jl through an external code generation handler. Loading the package registers that handler from its __init__ (via grpc_register_service_codegen()), so you do not normally call anything by hand — you just run ProtoBuf.jl's protojl on your .proto files.
When gRPCClient.jl is also loaded, a single protojl run emits the message types, the gRPCClient.jl client stubs, and the gRPCServer.jl registration functions into the same generated file.
Running protojl
Load both packages, then run protojl (re-exported by gRPCServer) from your project directory:
using ProtoBufusing gRPCServerimport gRPCClientmkdir("generated") # protojl requires the output directory to pre-existprotojl("myservice.proto", ".", "generated"; always_use_modules = true, add_kwarg_constructors = true)For a package myservice; in the proto, this writes:
generated/
└── myservice/
├── myservice.jl # module wrapper; include() + using .myservice
└── myservice_pb.jl # messages + client stubs + registration functionsThe generated file is delimited into two blocks:
# gRPCClient.jl BEGIN…# gRPCClient.jl END— the<Service>_<Rpc>_Clientclient constructors (present only when gRPCClient.jl is loaded).# gRPCServer.jl BEGIN…# gRPCServer.jl END— the<Service>_<Rpc>_Methoddescriptor builders, the per-RPCregister_<Service>_<Rpc>!functions, and the aggregateregister_<Service>!function.
Use the generated module from your server:
using gRPCServerinclude("generated/myservice/myservice.jl")using .myserviceRegenerate whenever you change the .proto file; the output is deterministic (no timestamps or machine-specific paths).
Emitted symbols
For each service in the .proto, gRPCServer emits the following (signatures as emitted in the generated output; the emitted keyword definitions annotate the raw flags as ::Bool):
- Typed descriptor builder — one per RPC:
<Service>_<Rpc>_Method(handler; raw_request=false, raw_response=false) -> gRPCServer.MethodDescriptor- Per-RPC registration — emitted in both argument orders, so the do-block
form works:
register_<Service>_<Rpc>!(server::GRPCServer, handler; raw_request=false, raw_response=false) -> server
register_<Service>_<Rpc>!(handler::Function, server::GRPCServer; kwargs...) -> server- Per-service aggregate — registers several RPCs at once by keyword:
register_<Service>!(server::GRPCServer; <Rpc>=nothing, ...) -> serverEvery non-nothing keyword registers its RPC. Each keyword accepts a handler or a (handler, raw_request, raw_response) tuple (raw flags per method). All-nothing is a no-op. It is equivalent to calling the per-RPC register_<Service>_<Rpc>! functions individually.
Handler contracts
The registration functions validate the handler signature at registration time; a mismatched shape throws ArgumentError. The four contracts (context first):
| RPC type | Handler signature |
|---|---|
| Unary | (ctx::gRPCServer.ServerContext, req::TReq) -> TResp |
| Server streaming | (ctx, req::TReq, stream::gRPCServer.ServerStream{TResp}) -> Nothing — send responses with gRPCServer.send!(stream, msg) |
| Client streaming | (ctx, stream::gRPCServer.ClientStream{TReq}) -> TResp — iterate with for req in stream |
| Bidirectional | (ctx, stream::gRPCServer.BidiStream{TReq, TResp}) -> Nothing — iterate and gRPCServer.send!(stream, msg) |
Raw request and response buffers
The raw_request and raw_response flags override a side with Vector{UInt8}: the handler receives the raw, undecoded protobuf payload and/or returns raw response bytes instead of a typed message. The raw buffer is the protobuf message body only; the gRPC framing is still handled by the library.
# Both sides rawregister_myservice_GetThing!(server, (ctx, raw) -> raw; raw_request = true, raw_response = true)Registration-time validation
A handler whose signature does not match the RPC's contract (wrong arity, wrong types, or a raw/typed mismatch) raises ArgumentError at register_*! time, before any request is served.
Under the hood
Each generated register_<Service>_<Rpc>! builds a MethodDescriptor via the *_Method builder and calls gRPCServer.register_method! on the server's dispatcher. The runtime interface — register_method!, MethodDescriptor, ServiceDescriptor, register! — is documented in the API Reference; an explicit walkthrough of the underlying layer lives in the Advanced Examples.
Next steps
- Quick Start — end-to-end walkthrough
- Examples — five runnable example servers
- TLS — serving codegen-registered services over TLS
- API Reference — the runtime interface beneath the codegen