Skip to main content

gRPC Tool

The bal grpc tool generates Ballerina code from Protocol Buffer (.proto) files. It creates service stubs with RPC method signatures, client connectors for calling gRPC services, and Ballerina record types that correspond to protobuf message definitions. This lets you integrate with gRPC-based microservices using idiomatic Ballerina code.

note

gRPC code generation is not available through the Visual Designer — + Add Artifact does not include a gRPC option. Use the integrated terminal in your project to run bal grpc commands directly.


Example proto file

The examples throughout this page use the following proto file:


syntax = "proto3";

package orders;

service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc CreateOrder (CreateOrderRequest) returns (Order);
rpc ListOrders (ListOrdersRequest) returns (stream Order);
rpc StreamUpdates (stream OrderUpdate) returns (stream StatusUpdate);
}

message GetOrderRequest {
string order_id = 1;
}

message CreateOrderRequest {
string customer_id = 1;
repeated LineItem items = 2;
}

message LineItem {
string product_id = 1;
int32 quantity = 2;
double unit_price = 3;
}

message Order {
string id = 1;
string customer_id = 2;
repeated LineItem items = 3;
double total = 4;
OrderStatus status = 5;
}

message OrderUpdate {
string order_id = 1;
OrderStatus new_status = 2;
}

message StatusUpdate {
string order_id = 1;
OrderStatus status = 2;
}

message ListOrdersRequest {
string customer_id = 1;
int32 limit = 2;
}

enum OrderStatus {
PENDING = 0;
CONFIRMED = 1;
SHIPPED = 2;
DELIVERED = 3;
CANCELLED = 4;
}

Generating a service stub from a proto file

Use this when you want to expose a gRPC service from your integrator project.

Step 1: Place the proto file in your project

Copy your .proto file into a resources/ folder inside your integrator project:

my-integration/
├── Ballerina.toml
├── main.bal
└── resources/
└── order_service.proto

Step 2: Open the terminal at the project root

In VS Code open the integrated terminal (Ctrl+`) and confirm you are in the project root — the directory that contains Ballerina.toml:

ls Ballerina.toml

Step 3: Run the tool

bal grpc --input resources/order_service.proto --mode service --output .

Step 4: Verify generated files

The tool places two files in the project root:

my-integration/
├── Ballerina.toml
├── main.bal
├── resources/
│ └── order_service.proto
├── order_service_pb.bal # Message types (records)
└── order_service_service.bal # Service stub with empty RPC methods

Both files are compiled automatically as part of the project package — no extra imports or configuration needed.

Step 5: Implement the service

Open order_service_service.bal and fill in the generated stub methods with your integration logic:

import ballerina/grpc;

listener grpc:Listener ep = new (9090);

@grpc:Descriptor {value: ORDER_SERVICE_DESC}
service "OrderService" on ep {

remote function GetOrder(GetOrderRequest request) returns Order|error {
// TODO: Implement
}

remote function CreateOrder(CreateOrderRequest request) returns Order|error {
// TODO: Implement
}

remote function ListOrders(ListOrdersRequest request) returns stream<Order, error?>|error {
// TODO: Implement
}

remote function StreamUpdates(stream<OrderUpdate, grpc:Error?> clientStream) returns stream<StatusUpdate, error?>|error {
// TODO: Implement
}
}


Generating a client from a proto file

Use this when you want to call an external gRPC service from your integrator project.

Step 1: Place the proto file in your project

my-integration/
├── Ballerina.toml
├── main.bal
└── resources/
└── order_service.proto

Step 2: Open the terminal at the project root

ls Ballerina.toml

Step 3: Run the tool

bal grpc --input resources/order_service.proto --mode client --output .

Step 4: Verify generated files

my-integration/
├── Ballerina.toml
├── main.bal
├── resources/
│ └── order_service.proto
├── order_service_pb.bal # Message types (records)
└── order_service_client.bal # Type-safe client connector

Step 5: Use the client in your integration

Open the existing connections.bal file in your project root and add the client initialisation. This makes the client available as a module-level variable across all flows and artifacts:

my-integration/
├── Ballerina.toml
├── main.bal
├── connections.bal ← open this file
├── resources/
│ └── order_service.proto
├── order_service_pb.bal
└── order_service_client.bal

In connections.bal:

configurable string orderServiceUrl = ?;

final OrderServiceClient orderClient = check new (orderServiceUrl);

Add the service URL to your Config.toml:

orderServiceUrl = "http://localhost:9090"

Unary RPC:

function getOrder(string orderId) returns Order|error {
return check orderClient->GetOrder({order_id: orderId});
}

function createOrder(string customerId, LineItem[] items) returns Order|error {
return check orderClient->CreateOrder({
customer_id: customerId,
items: items
});
}

Server streaming:

function listCustomerOrders(string customerId) returns Order[]|error {
stream<Order, error?> orderStream = check orderClient->ListOrders(
{customer_id: customerId, 'limit: 50}
);
Order[] orders = [];
check orderStream.forEach(function(Order 'order) {
orders.push('order);
});
return orders;
}

Bidirectional streaming:

function streamOrderUpdates(OrderUpdate[] updates) returns OrderStatus[]|error {
StreamUpdatesStreamingClient streamClient = check orderClient->StreamUpdates();
foreach OrderUpdate update in updates {
check streamClient->sendOrderUpdate(update);
}
check streamClient->complete();

OrderStatus[] statuses = [];
OrderStatus|error? response = streamClient->receiveOrderStatus();
while response is OrderStatus {
statuses.push(response);
response = streamClient->receiveOrderStatus();
}
return statuses;
}

Generating only the stub file

Omit --mode to generate only the stub file:

bal grpc --input resources/order_service.proto --output .

This produces order_service_pb.bal file.


Proto import paths

If your proto file imports other proto files, add --proto_path for each import directory:

bal grpc --input resources/order_service.proto \
--proto_path ./resources/ \
--proto_path ./third_party/ \
--output .

Regenerating after proto changes

Re-run the same command whenever the .proto file changes — the generated files are overwritten automatically.

Do not edit order_service_pb.bal directly. It is always overwritten on regeneration. Put all custom logic in separate files.


Command reference

bal grpc --input <proto-file> [options]
FlagAliasRequiredDefaultDescription
--input-iYesPath to the .proto file
--output-oNoCurrent directoryOutput directory for generated files
--modeNoBothGeneration mode: client, service, or omit for both
--proto-pathNoPath to a directory containing imported .proto files

Protobuf to Ballerina type mapping

Protobuf typeBallerina type
doublefloat
floatfloat
int32, sint32, sfixed32int
int64, sint64, sfixed64int
uint32, fixed32int
uint64, fixed64int
boolboolean
stringstring
bytesbyte[]
enumenum
messagerecord {}
repeated TT[]
map<K, V>map<V>
oneofUnion type
google.protobuf.Anyanydata
google.protobuf.Timestamptime:Utc
google.protobuf.Durationdecimal
google.protobuf.Structmap<anydata>

gRPC communication patterns

PatternProto definitionBallerina signature
Unaryrpc Method(Req) returns (Res)remote function Method(Req) returns Res|error
Server streamingrpc Method(Req) returns (stream Res)remote function Method(Req) returns stream<Res, error?>|error
Client streamingrpc Method(stream Req) returns (Res)remote function Method(stream<Req, error?>) returns Res|error
Bidirectionalrpc Method(stream Req) returns (stream Res)remote function Method(stream<Req, error?>) returns stream<Res, error?>|error

What's next