Typescript gRPC
gRPC, which stands for "Google Remote Procedure Call," is an open-source framework developed by Google for enabling efficient and high-performance communication between distributed systems. It is used for building and connecting services, microservices, and applications across different programming languages and platforms.
For reasons to use gRPC, please visit my article Why Should I Use gRPC ?
Let's start our tutorial.
So our first step is to build our project folder, let's call it trpc-api. API is (Application Programming Interface) and REST API, GraphQL API, gRPC/tRPC API are all API's.
Our next step is to install some important dependancies inside our project /trpc-api:
yarn init -y
and our next command:yarn add --dev @grpc/grpc-js @grpc/proto-loader typescript ts-node
@grpc/grpc-js is a JavaScript library for working with gRPC (Google Remote Procedure Call) in Node.js.Some key features of @grpc/grpc-js include:
- Bi-directional streaming
- Protocol Buffers (ProtoBuf) support
- Promise-based API
- Interceptors
- Load balancing
Read more about @grpc/grpc-js
@grpc/proto-loader is a Node.js module that provides functionality for loading Protocol Buffer (ProtoBuf) files and generating gRPC service client and server code dynamically.
Read more about @grpc/proto-loader
Our next step is to create these files and folders inside our project /trpc-api:
create server.ts
create client.ts
create proto folder and random.proto file
First we will start with the random.proto or you can name it as you want. This file is inside /trpc-api/proto/random.proto and has the following code:
syntax = "proto3";
package randomPackage;
service Random {
rpc PingPong(PingRequet) returns (PongResponse) {};
}
message PingRequet {
string message = 1;
}
message PongResponse {
string message = 1;
}
To read more detailed explanation of the code, click here
After this step is complete and our random.proto is ready, next step is:
yarn proto-loader-gen-types --grpcLib=@grpc/grpc-js
--outDir=proto/ proto/*.proto
To read more about this command, click here
This command we execute in our Git Bash cli in our main directory /trpc-api. This is a command for generating TypeScript type definitions from Protocol Buffers (ProtoBuf) random.proto schema. After executing this command, we should see new files and folders in our proto folder:
/proto/random.ts
/proto/randomPackage/PingRequet.ts
/proto/randomPackage/PongResponse.ts
/proto/randomPackage/Random.ts
with all that.. we are pretty much done here and we can start coding our server.
Our next step is creating the server.ts if we didn't do that already in directory /trpc-api and the following code:
import path from "path";
import { Server, ServerCredentials, loadPackageDefinition } from "@grpc/grpc-js";
import { loadSync } from "@grpc/proto-loader";
import { ProtoGrpcType } from "./proto/random";
import { RandomHandlers } from "./proto/randomPackage/Random";
interface MainFunction {
(): void;
}
const main: MainFunction = () => {
const server: Server = getServer();
server.bindAsync(
"0.0.0.0:9000",
ServerCredentials.createInsecure(),
(err) => {
if (err) {
console.error(err);
return;
}
console.log("Your server started on port 9000");
server.start();
}
);
};
interface GetServerFunction {
(): Server;
}
const getServer: GetServerFunction = () => {
const server: Server = new Server();
const packageDef: any = loadSync(
path.resolve(__dirname, "./proto/random.proto")
);
const grpcObj = loadPackageDefinition(packageDef) as unknown as ProtoGrpcType;
const randomPackage = grpcObj.randomPackage;
server.addService(randomPackage.Random.service, {
PingPong: (call, callback) => {
console.log(call.request);
callback(null, { message: "Pong" });
},
} as RandomHandlers);
return server;
};
main();
For a detailed explanation of this code please click here
After our server.ts is complete, our next step is creating our client.ts file if we didn't do that already in directory: /trpc-api and the following code:
import path from "path";
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
import { ProtoGrpcType } from "./proto/random";
import { PingRequet } from "./proto/randomPackage/PingRequet";
import { PongResponse } from "./proto/randomPackage/PongResponse";
const packageDef = protoLoader.loadSync(
path.resolve(__dirname, "./proto/random.proto")
);
const grpcObj = grpc.loadPackageDefinition(
packageDef
) as unknown as ProtoGrpcType;
const client = new grpcObj.randomPackage.Random(
"0.0.0.0:9000",
grpc.credentials.createInsecure()
);
const deadline: Date = new Date();
deadline.setSeconds(deadline.getSeconds() + 5);
client.waitForReady(deadline, (err: Error | undefined) => {
if (err) {
console.error(err);
return;
}
onClientReady();
});
function onClientReady() {
const request: PingRequet = { message: "Ping" };
client.PingPong(
request,
(
err: grpc.ServerErrorResponse | null,
response: PongResponse | undefined
) => {
if (err) {
console.error(err);
return;
}
console.log(response);
}
);
}
For a detailed explanation of this code please click here
Well.. by now we should have our /proto complete, our client.ts and our server.ts complete and what is left is to run our code. In our package.json inside /trpc-api we should have:
{
"name": "ts-go-grpc",
"version": "1.0.0",
"main": "index.js",
"author": "cholakovit",
"license": "MIT",
"scripts": {
"server": "ts-node server",
"client": "ts-node client"
},
"devDependencies": {
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
}
}
For a detailed explanation of this code please click here
So, if we have all that, in the Git Bush cli /trpc-api type this
npm run server
Open a second Git Bush cli in /trpc-api and type this
npm run client
and everything should work fine!
To download the full code, click here