🎉 RamAPI v1.0 is now available! Read the Getting Started Guide
Documentation
Protocols
gRPC

gRPC

High-performance RPC with Protocol Buffers for microservices communication. RamAPI automatically generates proto files and handles gRPC server setup.

Table of Contents

  1. Overview
  2. Setup
  3. Proto Generation
  4. Service Definitions
  5. gRPC Clients

Overview

gRPC uses Protocol Buffers for efficient, type-safe communication:

service UserService {
  rpc GetUser (GetUserRequest) returns (GetUserResponse);
  rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
}

Benefits:

  • 2-3x faster than REST/JSON
  • Type-safe contracts
  • Built-in streaming
  • Perfect for microservices

Setup

Enable gRPC

import { createApp } from 'ramapi';
 
const app = createApp({
  protocols: {
    grpc: {
      port: 50051,           // gRPC port
      host: '0.0.0.0',       // Bind address
    },
  },
});
 
await app.listen(3000);
// HTTP: localhost:3000
// gRPC: localhost:50051

Install Dependencies

npm install @grpc/grpc-js @grpc/proto-loader

Proto Generation

Automatic Generation

RamAPI automatically generates proto files from your operations:

import { z } from 'zod';
 
const getUserOperation = {
  name: 'getUser',
 
  input: z.object({
    id: z.string().uuid(),
  }),
 
  output: z.object({
    id: z.string(),
    name: z.string(),
    email: z.string(),
  }),
 
  handler: async (input) => {
    return await db.users.findById(input.id);
  },
 
  grpc: {
    service: 'UserService',
    method: 'GetUser',
  },
};
 
// Automatically generates:
// service UserService {
//   rpc GetUser (GetUserRequest) returns (GetUserResponse);
// }

Development Mode

In development, protos are generated at runtime:

// Automatic proto generation and loading
const app = createApp({
  protocols: {
    grpc: { port: 50051 },
  },
});
 
// Proto files generated in .ramapi/protos/

Production Mode

For production, pre-compile protos during build:

# Add to package.json
{
  "scripts": {
    "build:protos": "ramapi build-protos"
  }
}
// Uses pre-compiled protos (faster startup)
NODE_ENV=production node server.js

Service Definitions

Simple Service

const operations = [
  {
    name: 'getUser',
    input: z.object({ id: z.string() }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => await db.users.findById(input.id),
    grpc: {
      service: 'UserService',
      method: 'GetUser',
    },
  },
];

Generated Proto:

syntax = "proto3";

package ramapi;

service UserService {
  rpc GetUser (GetUserRequest) returns (GetUserResponse);
}

message GetUserRequest {
  string id = 1;
}

message GetUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

Full CRUD Service

const userOperations = [
  // List users
  {
    name: 'listUsers',
    output: z.object({
      users: z.array(z.object({
        id: z.string(),
        name: z.string(),
      })),
    }),
    handler: async () => ({
      users: await db.users.findAll(),
    }),
    grpc: { service: 'UserService', method: 'ListUsers' },
  },
 
  // Get user
  {
    name: 'getUser',
    input: z.object({ id: z.string() }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => await db.users.findById(input.id),
    grpc: { service: 'UserService', method: 'GetUser' },
  },
 
  // Create user
  {
    name: 'createUser',
    input: z.object({
      name: z.string(),
      email: z.string(),
    }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => await db.users.create(input),
    grpc: { service: 'UserService', method: 'CreateUser' },
  },
 
  // Update user
  {
    name: 'updateUser',
    input: z.object({
      id: z.string(),
      name: z.string().optional(),
      email: z.string().optional(),
    }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => {
      const { id, ...updates } = input;
      return await db.users.update(id, updates);
    },
    grpc: { service: 'UserService', method: 'UpdateUser' },
  },
 
  // Delete user
  {
    name: 'deleteUser',
    input: z.object({ id: z.string() }),
    output: z.object({ success: z.boolean() }),
    handler: async (input) => {
      await db.users.delete(input.id);
      return { success: true };
    },
    grpc: { service: 'UserService', method: 'DeleteUser' },
  },
];
 
// Register all operations
const protocolManager = app.getProtocolManager();
userOperations.forEach(op => protocolManager?.registerOperation(op));

gRPC Clients

Node.js Client

import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
 
// Load proto
const packageDefinition = protoLoader.loadSync('user.proto');
const proto = grpc.loadPackageDefinition(packageDefinition);
 
// Create client
const client = new proto.ramapi.UserService(
  'localhost:50051',
  grpc.credentials.createInsecure()
);
 
// Call methods
client.GetUser({ id: '123' }, (error, response) => {
  if (error) {
    console.error('Error:', error);
  } else {
    console.log('User:', response);
  }
});
 
// Async/await wrapper
function getUserAsync(id: string): Promise\<any\> {
  return new Promise((resolve, reject) => {
    client.GetUser({ id }, (error, response) => {
      if (error) reject(error);
      else resolve(response);
    });
  });
}
 
const user = await getUserAsync('123');

grpcurl (CLI Testing)

# Install grpcurl
brew install grpcurl
 
# List services
grpcurl -plaintext localhost:50051 list
 
# List methods
grpcurl -plaintext localhost:50051 list UserService
 
# Call method
grpcurl -plaintext \
  -d '{"id":"123"}' \
  localhost:50051 \
  ramapi.UserService/GetUser

Other Languages

gRPC supports many languages:

Python:

import grpc
import user_pb2
import user_pb2_grpc
 
channel = grpc.insecure_channel('localhost:50051')
stub = user_pb2_grpc.UserServiceStub(channel)
 
response = stub.GetUser(user_pb2.GetUserRequest(id='123'))
print(response)

Go:

conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
client := pb.NewUserServiceClient(conn)
 
response, _ := client.GetUser(context.Background(), &pb.GetUserRequest{
    Id: "123",
})
fmt.Println(response)

Complete Example

import { createApp } from 'ramapi';
import { z } from 'zod';
 
const app = createApp({
  protocols: {
    grpc: {
      port: 50051,
    },
  },
});
 
// Define operations
const operations = [
  {
    name: 'getUser',
    input: z.object({ id: z.string() }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => await db.users.findById(input.id),
    grpc: { service: 'UserService', method: 'GetUser' },
  },
  {
    name: 'createUser',
    input: z.object({
      name: z.string(),
      email: z.string(),
    }),
    output: z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
    handler: async (input) => await db.users.create(input),
    grpc: { service: 'UserService', method: 'CreateUser' },
  },
];
 
// Register operations
const protocolManager = app.getProtocolManager();
operations.forEach(op => protocolManager?.registerOperation(op));
 
await app.listen(3000);
console.log('gRPC server: localhost:50051');

Test with grpcurl:

# Get user
grpcurl -plaintext -d '{"id":"123"}' localhost:50051 ramapi.UserService/GetUser
 
# Create user
grpcurl -plaintext -d '{"name":"John","email":"john@example.com"}' \
  localhost:50051 ramapi.UserService/CreateUser

Next Steps


Need help? Check the Troubleshooting Guide or GitHub Issues (opens in a new tab).