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
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.
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.
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:
| File | Purpose |
|---|---|
service.ts | Interfaces, enums, unions, and schema helpers |
service_grpc.ts | Node.js @grpc/grpc-js server/client code |
service_grpc_web.ts | Browser 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.
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}`); }, );
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.
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);
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"); });
Generated service code only replaces request and response serialization. Normal gRPC operational features still belong to the transport package:
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 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.
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.