Skip to content
Documentation
Get Started

Get Started

Before this, I hope you have some understanding of gRPC and protobuf, which is the best, of course, you can also get familiar with it here. Here we will use a simple example for a quick start, divided into four steps, which are project initialization, loading proto, implementing the server, and implementing the client.

πŸ’‘

Learn more by visiting and reading: gRPC & proto3

Project Initialization

Create Project

Create a demo project and enter:

Terminal
mkdir demo && cd demo

Initialize and install dependencies

Terminal
npm init -y
npm i grpcity
⚠️

Note: We need to add "type": "module" in package.json to activate ESM.

Project Directory

We need to create a series of directories and files. The final project directory and file structure are as follows. The highlighted parts need to be created by us in advance:

Terminal
.
β”œβ”€β”€ client.js
β”œβ”€β”€ loader.js
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ package.json
β”œβ”€β”€ proto
β”‚   └── helloworld
β”‚       β”œβ”€β”€ model
β”‚       β”‚   └── message.proto
β”‚       └── service.proto
└── server.js

Load Proto

Define Proto

We define two services, Greeter and Hellor, and split message into model.

Enter the following content for service.proto:

./proto/helloworld/service.proto
syntax = "proto3";
 
package helloworld;
 
import "helloworld/model/message.proto";
 
service Greeter {
  rpc SayGreet(HelloRequest) returns (HelloReply) {}
}
 
service Hellor {
  rpc SayHello(HelloRequest) returns (HelloReply) {}
}

Enter the following content for message.proto:

./proto/helloworld/model/message.proto
syntax = "proto3";
 
package helloworld.model;
 
message HelloRequest {
  string name = 1;
}
 
message HelloReply {
  string message   = 1;
  int32 count = 2;
}

Implement Loader

Enter the following content for ./loader.js:

./loader.js
import { ProtoLoader } from 'grpcity'
import path from 'node:path'
 
// __dirname for esm
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
 
export default new ProtoLoader({
    location: path.join(__dirname, './proto'),
    files: [
        'helloworld/service.proto'
    ]
})
πŸ’‘

In ProtoLoader, location and files respectively represent the proto directory and service proto file.

So far, we have completed the loading of proto files and got loader.js. This way we can import this file anywhere and support us to develop and implement the client or server.

Implement Server

All the steps here will be done in the ./server.js file.

Import Loader

./server.js
import loader from "./loader.js"

Implement Greeter

Create Greeter and implement the sayGreet method:

./server.js
class Greeter {
    constructor() {
        this.count = 0
    }
 
    async sayGreet(call) {
        const { name } = call.request
        this.count++
        return {
            message: `hello ${name || "world"} by Greeter`,
            count: this.count
        }
    }
}

Implement Hellor

Continue to implement Hellor in the same way

./server.js
class Hellor {
    async sayHello(call) {
        const { name } = call.request
        return { message: `hello ${name || "world"} by Hellor` }
    }
}

Bind and Start

./server.js
const start = async (addr) => {
    // loader initialization
    await loader.init()
 
    // server initialization and get instance
    const server = await loader.initServer()
 
    // bind class methods with service
    server.add('helloworld.Greeter', new Greeter())
    server.add('helloworld.Hellor', new Hellor())
 
    // listen
    await server.listen(addr)
    console.log('helloworld server is started: ', addr)
}
 
// start
start('127.0.0.1:9098')

Implement Client

Import Loader

./client.js
import loader from "./loader.js"

Get Client

Create start method, complete getting client.

./client.js
const start = async (addr) => {
    await loader.init()
 
    const clients = await loader.initClients({
        services: {
            'helloworld.Greeter': addr,
            'helloworld.Hellor': addr
        }
    })
}

Call and Start

We then supplement the logic of the client calling the server in the start method and print the call result.

./client.js
const start = async (addr) => {
 
    // ...
    // loader init
    // clients init
    // ....
 
    // greeter client
    const greeterClient = clients.get('helloworld.Greeter')
    const greeterResult = await greeterClient.sayGreet({ name: 'grpcity' })
    console.log('greeterClient.sayGreet', greeterResult.response)
 
    // hellor client
    const hellorClient = clients.get('helloworld.Hellor')
    const hellorResult = await hellorClient.sayHello({ name: 'grpcity' })
    console.log('hellorClient.sayHello', hellorResult.response)
}
 
// execute
start('127.0.0.1:9098')
πŸ’‘

The result received after the client initiates the call includes four values, which are status, peer, metadata, and response. Here, we only need to print out response.

Debug

We need two terminal windows, one for running the server and the other for running the client.

Start Server

Terminal
node ./server.js
helloworld server is started:  127.0.0.1:9098

After starting, our client can connect to the corresponding address.

Start Client

Start the client, establish a connection with the server, and execute the call. Here, we will execute the client twice.

The result of the first execution is as follows:

Terminal
node ./client.js
greeterClient.sayGreet { message: 'hello grpcity by Greeter', count: 1 }
hellorClient.sayHello { message: 'hello grpcity by Hellor' }

The result of the second execution is as follows:

Terminal
node ./client.js
greeterClient.sayGreet { message: 'hello grpcity by Greeter', count: 2 }
hellorClient.sayHello { message: 'hello grpcity by Hellor' }

We can observe that both executions return as expected. In the sayGreet function, the count is incremented to 2 after the second run, aligning with our expectations and confirming the successful completion of this client-server interaction loop.

And that wraps up our quick start tutorial, designed to be straightforward and user-friendly. For a deeper understanding, feel free to explore the User Guide.

The code for this tutorial is available on GitHub.