Protocols

gRPC Support

Build high-performance microservices with native gRPC support. Unary, streaming, and bidirectional RPC calls with Protocol Buffers.

Overview

Without proper gRPC support, you're manually setting up servers, handling Protocol Buffers, and managing streaming. With MoroJS, you get all of that automatically. Traditional gRPC setup requires complex configuration. We handle that automatically.

Set up gRPC with one call
1import { createApp } from '@morojs/moro';
2
3const app = await createApp();
4
5// Configure gRPC
6app.grpcInit({
7  port: 50051,
8  adapter: 'grpc-js',
9  enableHealthCheck: true
10});
11
12// Register a service
13await app.grpcService('./proto/users.proto', 'UserService', {
14  GetUser: async (call, callback) => {
15    const user = await db.users.findById(call.request.id);
16    callback(null, user);
17  }
18});
19
20app.listen(3000);
21// HTTP: http://localhost:3000
22// gRPC: localhost:50051

Without gRPC Support

  • Manual gRPC server setup
  • Complex Protocol Buffer handling
  • No built-in streaming support
  • Manual authentication setup

With MoroJS

  • One-call gRPC setup
  • Automatic Protocol Buffer handling
  • Built-in streaming support
  • Integrated authentication

Unary

Single request/response - REST-like operations

Server Streaming

Single request, stream responses - Data feeds

Client Streaming

Stream requests, single response - File uploads

Bidirectional

Stream both directions - Chat, real-time

How It Works

gRPC is a high-performance RPC framework that uses Protocol Buffers for serialization. MoroJS provides native gRPC support with automatic Protocol Buffer handling, streaming support, and integrated authentication. You define services in .proto files and implement handlers in your application.

Quick Start

Installation
1# Install gRPC dependencies
2npm install @grpc/grpc-js @grpc/proto-loader
Basic gRPC Server
1import { createApp } from '@morojs/moro';
2
3const app = await createApp();
4
5// Configure gRPC - synchronous, no await needed!
6app.grpcInit({
7  port: 50051,
8  host: '0.0.0.0',
9  adapter: 'grpc-js',
10  enableHealthCheck: true,
11  enableReflection: true
12});
13
14// Register a service
15await app.grpcService('./proto/users.proto', 'UserService', {
16  GetUser: async (call, callback) => {
17    const user = await db.users.findById(call.request.id);
18    callback(null, user);
19  },
20  ListUsers: async (call) => {
21    const users = await db.users.findAll();
22    for (const user of users) {
23      call.write(user);
24    }
25    call.end();
26  }
27});
28
29// Start both HTTP and gRPC servers
30app.listen(3000, () => {
31  console.log('HTTP: http://localhost:3000');
32  console.log('gRPC: localhost:50051');
33});

Service Definition

Proto File Example
1syntax = "proto3";
2
3package user;
4
5service UserService {
6  // Unary call
7  rpc GetUser (GetUserRequest) returns (User) {}
8
9  // Server streaming
10  rpc ListUsers (ListUsersRequest) returns (stream User) {}
11
12  // Client streaming
13  rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse) {}
14
15  // Bidirectional streaming
16  rpc Chat (stream ChatMessage) returns (stream ChatMessage) {}
17}
18
19message GetUserRequest {
20  string id = 1;
21}
22
23message User {
24  string id = 1;
25  string name = 2;
26  string email = 3;
27  int32 age = 4;
28}
29
30message ListUsersRequest {
31  int32 page = 1;
32  int32 limit = 2;
33}
34
35message CreateUserRequest {
36  string name = 1;
37  string email = 2;
38}
39
40message CreateUsersResponse {
41  int32 created = 1;
42  repeated string ids = 2;
43}
44
45message ChatMessage {
46  string user_id = 1;
47  string message = 2;
48  int64 timestamp = 3;
49}

Server Implementation

Unary RPC
1await app.grpcService('./proto/users.proto', 'UserService', {
2  GetUser: async (call, callback) => {
3    try {
4      const { id } = call.request;
5
6      // Fetch from database
7      const user = await db.users.findById(id);
8
9      if (!user) {
10        return callback({
11          code: grpc.status.NOT_FOUND,
12          message: 'User not found'
13        });
14      }
15
16      callback(null, user);
17    } catch (error) {
18      callback({
19        code: grpc.status.INTERNAL,
20        message: error.message
21      });
22    }
23  }
24});
Server Streaming RPC
1{
2  ListUsers: async (call) => {
3    const { page, limit } = call.request;
4
5    try {
6      const users = await db.users
7        .find()
8        .skip(page * limit)
9        .limit(limit);
10
11      // Stream each user
12      for (const user of users) {
13        call.write({
14          id: user.id,
15          name: user.name,
16          email: user.email,
17          age: user.age
18        });
19      }
20
21      // End stream
22      call.end();
23    } catch (error) {
24      call.destroy(error);
25    }
26  }
27}
Bidirectional Streaming RPC
1{
2  Chat: async (call) => {
3    const userId = call.metadata.get('user-id');
4
5    // Receive messages
6    call.on('data', (message) => {
7      console.log(`User ${userId}: ${message.message}`);
8
9      // Broadcast to other clients
10      broadcastToRoom(message);
11
12      // Echo back confirmation
13      call.write({
14        user_id: 'system',
15        message: `Message received: ${message.message}`,
16        timestamp: Date.now()
17      });
18    });
19
20    call.on('end', () => {
21      call.end();
22    });
23
24    call.on('error', (error) => {
25      console.error('Chat error:', error);
26    });
27  }
28}

Client Usage

Creating a Client
1// Create a gRPC client
2const client = await app.createGrpcClient(
3  './proto/users.proto',
4  'UserService',
5  'localhost:50051',
6  {
7    credentials: 'insecure' // or provide TLS credentials
8  }
9);
10
11// Use the client
12const user = await new Promise((resolve, reject) => {
13  client.GetUser({ id: '123' }, (error, response) => {
14    if (error) reject(error);
15    else resolve(response);
16  });
17});
18
19console.log(user.name);
Server Streaming Call
1const call = client.ListUsers({ page: 0, limit: 10 });
2
3call.on('data', (user) => {
4  console.log('Received user:', user.name);
5});
6
7call.on('end', () => {
8  console.log('Stream ended');
9});
10
11call.on('error', (error) => {
12  console.error('Stream error:', error);
13});

Authentication & Middleware

Token-Based Authentication
1import { grpcAuth, extractTokenFromMetadata } from '@morojs/moro';
2
3// Server side
4await app.grpcService('./proto/users.proto', 'UserService', {
5  GetUser: grpcAuth({
6    extractToken: extractTokenFromMetadata,
7    verifyToken: async (token) => {
8      return await verifyJWT(token, process.env.JWT_SECRET);
9    }
10  })(async (call, callback) => {
11    console.log('Authenticated user:', call.user);
12    const user = await db.users.findOne({ id: call.request.id });
13    callback(null, user);
14  })
15});
16
17// Client side
18const metadata = new grpc.Metadata();
19metadata.add('authorization', `Bearer ${token}`);
20
21client.GetUser({ id: '123' }, metadata, (error, response) => {
22  // ...
23});
Role-Based Access
1import { grpcRequireRole } from '@morojs/moro';
2
3await app.grpcService('./proto/users.proto', 'UserService', {
4  // Requires 'admin' role
5  DeleteUser: grpcRequireRole('admin')(async (call, callback) => {
6    await db.users.deleteOne({ id: call.request.id });
7    callback(null, { success: true });
8  })
9});

Features

Core Features

  • Proto-based service definitions
  • Unary, server streaming, client streaming, bidirectional
  • Authentication and middleware support
  • Built-in health check service
  • Server reflection for debugging
  • TLS/SSL support

Call Types

Unary
Single request/response - REST-like operations
Server Streaming
Single request, stream responses - Data feeds
Client Streaming
Stream requests, single response - File uploads
Bidirectional
Stream both directions - Chat, real-time

Next Steps