title: gRPC Support sidebar_position: 25 id: grpc_support license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Fory can generate JavaScript service companions for schemas that define services. The generated service code uses normal gRPC transports while request and response objects are serialized with Fory instead of protobuf.

Use this mode when both RPC peers are generated from the same Fory IDL, protobuf IDL, or FlatBuffers IDL and both sides expect Fory-encoded message bodies. Use normal protobuf gRPC generation for APIs that must be consumed by generic protobuf clients, reflection tools, or components that expect protobuf message bytes.

Use --grpc for Node.js server and client code. Use --grpc-web for browser clients that call a gRPC-Web compatible server or proxy.

Add Dependencies

The generated model file depends on @apache-fory/core.

Node.js gRPC companions import @grpc/grpc-js:

npm install @apache-fory/core @grpc/grpc-js

Browser gRPC-Web companions import grpc-web:

npm install @apache-fory/core grpc-web

Fory does not add gRPC packages as hard dependencies. Add only the transport package used by your application.

Define a Service

Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers rpc_service definitions. A Fory IDL service looks like this:

package demo.greeter;

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string reply = 1;
}

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

Generate Node.js gRPC bindings:

foryc service.fdl --javascript_out=./generated/javascript --grpc

Generate browser gRPC-Web bindings:

foryc service.fdl --javascript_out=./generated/javascript --grpc-web

Generate both:

foryc service.fdl --javascript_out=./generated/javascript --grpc --grpc-web

For service.fdl, JavaScript output contains:

FilePurpose
service.tsInterfaces, enums, unions, and schema helpers
service_grpc.tsNode.js @grpc/grpc-js server/client code
service_grpc_web.tsBrowser grpc-web clients

The generated model file exports registerXxxTypes(fory) for custom Fory instances and default root helpers such as serializeHelloRequest and deserializeHelloRequest. Generated gRPC companions import those helpers automatically.

Implement a Node.js Server

import * as grpc from "@grpc/grpc-js";
import {
  GreeterHandlers,
  addGreeterService,
} from "./generated/javascript/service_grpc";

const greeter: GreeterHandlers = {
  sayHello(call, callback) {
    callback(null, {
      reply: `Hello, ${call.request.name}`,
    });
  },
};

const server = new grpc.Server();
addGreeterService(server, greeter);
server.bindAsync(
  "0.0.0.0:50051",
  grpc.ServerCredentials.createInsecure(),
  (error, port) => {
    if (error) {
      throw error;
    }
    server.start();
    console.log(`listening on ${port}`);
  },
);

Create a Node.js Client

import * as grpc from "@grpc/grpc-js";
import { createGreeterClient } from "./generated/javascript/service_grpc";

const client = createGreeterClient(
  "localhost:50051",
  grpc.credentials.createInsecure(),
);

client.sayHello({ name: "Fory" }, (error, reply) => {
  if (error) {
    throw error;
  }
  console.log(reply.reply);
});

Use normal @grpc/grpc-js metadata, call options, credentials, deadlines, and interceptors with the generated client and server.

Create a Browser Client

import { createGreeterWebClient } from "./generated/javascript/service_grpc_web";

const client = createGreeterWebClient("https://api.example.com", {
  wireFormat: "grpcweb",
});

client.sayHello({ name: "Fory" }, null, (error, reply) => {
  if (error) {
    console.error(error.message);
    return;
  }
  console.log(reply.reply);
});

For unary calls, the generated promise client is also available:

import { createGreeterWebPromiseClient } from "./generated/javascript/service_grpc_web";

const client = createGreeterWebPromiseClient("https://api.example.com");
const reply = await client.sayHello({ name: "Fory" });
console.log(reply.reply);

Streaming RPCs

Node.js companions support all gRPC streaming shapes:

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
  rpc LotsOfReplies (HelloRequest) returns (stream HelloReply);
  rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply);
  rpc Chat (stream HelloRequest) returns (stream HelloReply);
}

Browser gRPC-Web companions support unary and server-streaming methods. gRPC-Web does not support client-streaming or bidirectional methods; the compiler rejects those shapes for --grpc-web.

Node.js server implementations use the normal @grpc/grpc-js streaming call objects:

const greeter: GreeterHandlers = {
  sayHello(call, callback) {
    callback(null, { reply: `Hello, ${call.request.name}` });
  },

  lotsOfReplies(call) {
    call.write({ reply: `Hello, ${call.request.name}` });
    call.write({ reply: `Welcome, ${call.request.name}` });
    call.end();
  },

  lotsOfGreetings(call, callback) {
    const names: string[] = [];
    call.on("data", (request) => {
      names.push(request.name);
    });
    call.on("end", () => {
      callback(null, { reply: `Hello, ${names.join(", ")}` });
    });
  },

  chat(call) {
    call.on("data", (request) => {
      call.write({ reply: `Hello, ${request.name}` });
    });
    call.on("end", () => {
      call.end();
    });
  },
};

Node.js clients use the generated methods that match the RPC shape:

const replies = client.lotsOfReplies({ name: "Fory" });
replies.on("data", (reply) => {
  console.log(reply.reply);
});

const greetings = client.lotsOfGreetings((error, reply) => {
  if (error) {
    throw error;
  }
  console.log(reply.reply);
});
greetings.write({ name: "Alice" });
greetings.write({ name: "Bob" });
greetings.end();

const chat = client.chat();
chat.on("data", (reply) => {
  console.log(reply.reply);
});
chat.write({ name: "Alice" });
chat.write({ name: "Bob" });
chat.end();

For services with server-streaming methods, the generated gRPC-Web companion defaults to grpcwebtext wire format. Unary-only services default to grpcweb. You can choose the format explicitly:

const client = createGreeterWebClient("https://api.example.com", {
  wireFormat: "grpcwebtext",
});

Browser clients can consume server-streaming RPCs with the callback client:

const stream = client.lotsOfReplies({ name: "Fory" });

stream.on("data", (reply) => {
  console.log(reply.reply);
});
stream.on("error", (error) => {
  console.error(error.message);
});
stream.on("end", () => {
  console.log("stream ended");
});

Operations

Generated service code only replaces request and response serialization. Normal gRPC operational features still belong to the transport package:

  • TLS and credentials
  • Metadata and status codes
  • Deadlines and cancellation
  • Client and server interceptors
  • Load balancing and deployment-specific proxy configuration

Troubleshooting

Missing gRPC Packages

Add @grpc/grpc-js for Node.js companions or grpc-web for browser companions. @apache-fory/core intentionally does not depend on either transport package.

gRPC-Web Client-Streaming or Bidirectional RPCs Are Rejected

gRPC-Web does not support client-streaming or bidirectional streaming. Generate Node.js companions with --grpc for those shapes, or expose unary and server-streaming methods to browser clients.

Protobuf Clients Cannot Decode the Service

Fory gRPC companions do not use protobuf wire encoding for messages. Use a Fory-generated client for Fory-generated services, or provide a separate protobuf service endpoint for generic protobuf clients.