node_lrpc - v0.0.3

LRPC Framework

LRPC (Lightweight Remote Procedure Call) is a high-performance framework for handling inter-service communication efficiently. It provides an easy-to-use API for defining RPC endpoints, managing authentication, and handling real-time events.

  • High-performance request handling
  • Built-in authentication and authorization
  • Supports WebSockets for real-time communication
  • Queue-based request processing with RabbitMQ
  • Redis caching support
  • Flexible validation and permission system
  • Extensible with decorators for easy API development
npx create-node-lrpc create <application> <service>

/
│── lrpc.config.js
│── src
│ ├── controllers
│ │ ├── sampleController
│ │ │ ├── endpoints
│ │ │ │ ├── endpointCreate.ts
│ │ │ │ ├── endpointUpdate.ts
│ │ │ │ ├── endpointDelete.ts
│ │ │ ├── repository.ts
│ ├── lrpc
│ │ ├── clientFE
│ │ │ ├── api.ts
│ │ │ ├── index.ts
│ │ ├── serviceClient
│ │ │ ├── api.ts
│ │ │ ├── index.ts
│ │ │ ├── utils.ts
│ │ ├── registery.ts
│ ├── index.ts
│ ├── tests
│ │ ├── index.test.ts
│ │ ├── sampleController.ts
│ ├── utils
│ │ ├── index.ts
│ ├── .dockerignore
│ ├── .env
│ ├── Dockerfile
│ ├── jest.config.js
│ ├── lrpc.config.js
│ ├── package.json
│ └── tsconfig.json
npx lrpc init
import express from 'express';
import { initLRPC, initWorkers } from 'node_lrpc';
import bodyParser from 'body-parser';
import { PrismaClient } from '@prisma/client';
import { controllers, serviceClients } from './lrpc/registery';
import Container from 'typedi';

// console.log('Starting server');

export const prisma = new PrismaClient();
const app = express();
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));

app.get('/', (req, res) => {
res.send(`Hello World from ${process.env.REPLICAS}`);
});


export const LRPC = initLRPC({
application: 'smartbloks',
service: 'ai',
app
},
controllers,
serviceClients,
Container, {} as any);
// initWorkers(3, __filename);

LRPC can be configured via the lrpc.config.js file:

const { config } = require('dotenv');
config();

module.exports = {
application: 'applicationName',
service: 'api',
secret: 'mysecret',
appSecret: process.env.JWT_SECRET,
redisUrl: process.env.REDIS_URL
}

Endpoints in LRPC are defined using decorators and typed request-response models.

import { LRPCAuth, LRPCFunction, LRPCPayload, LRPCProp, LRPCPropArray } from "node_lrpc";
import { BaseResponse, HandlerConfig, LRPCRequest, Status, IEndpoint } from "node_lrpc";
import engineRepository from "../engineRepository";
import Container, { Service } from "typedi";

const controller = "engine";

@LRPCPayload(controller)
export class ImageChoices {
@LRPCProp
rprompt: string;

@LRPCProp
url: string;
}

@LRPCPayload(controller)
export class createImageRequest {
@LRPCProp
prompt: string;
}

@LRPCPayload(controller, true)
export class createImageResponse {
@LRPCPropArray(ImageChoices)
imageChoices: ImageChoices[];
}

@Service()
export class createImage implements HandlerConfig<createImageRequest, createImageResponse> {
_engineRepository: engineRepository;

constructor() {
this._engineRepository = Container.get(engineRepository);
}

async validator(input: createImageRequest): Promise<{ message: string; status: Status }> {
if (!input.prompt) {
return { message: "prompt is required", status: "validationError" };
}
return { message: "Validated successfully", status: "success" };
}

@LRPCAuth()
@LRPCFunction(controller, createImageRequest, createImageResponse)
async handler(data: LRPCRequest<createImageRequest>): Promise<BaseResponse<createImageResponse>> {
try {
const response = await this._engineRepository.createImage(data.payload);
return { message: "Created successfully.", status: "success", data: response };
} catch (error) {
console.log(error);
return { message: (error as any).message, status: "error" };
}
}
}

LRPC provides a CLI tool for managing controllers and endpoints.

npx lrpc init

This command initializes the LRPC configuration file (lrpc.config.js) if it does not already exist.

npx lrpc create <controller-name>

Creates a new controller with the specified name. It bootstraps the controller and its associated endpoints by creating a folder with the controller's name and generating four CRUD endpoint .ts files inside an endpoints subfolder.

npx lrpc endpoint <controller-name> <endpoint-name>

Creates a new endpoint inside the specified controller's folder. The command generates a .ts file with the endpoint name inside the controller's endpoints folder.

npx lrpc pull

Fetches scripts from other services and places them inside ./src/lrpc/serviceClient.

npx lrpc refresh

Updates the ./src/lrpc/registry file, which contains all the registered controllers.

npx lrpc unittest <controller-name>

Booststraps unit tests for the specified controller.

LRPC provides built-in authentication via AuthService:

const token = AuthService.sign({ userId: 123 });
const decoded = AuthService.verify(token, "/secure-endpoint");
const lrpc = new LRPCEngine();
lrpc.redis.set("key", "value");
lrpc.Queue.sendToQueue("taskQueue", { task: "processData" }, "procedureName");

This decorator is used to add authorization to an endpoint by specifying the roles that are allowed to access it.

class ExampleService {
@LRPCAuth(['admin', 'user'])
async someProtectedMethod(data: any) {
// Implementation
}
}
  • roles?: string[] - An optional array of roles that are authorized to access the method.

This decorator marks a class as part of the type definition in the payload or response of an endpoint.

@LRPCPayload('/user/create', true)
class CreateUserResponse {
id: string;
name: string;
}
  • path: string - The path of the endpoint.
  • isResponse?: boolean - Whether the class represents a response payload.

Marks a field in an LRPCPayload as optional.

class User {
@LRPCPropOp
middleName?: string;
}
  • target: any - The class prototype.
  • key: string - The field name.

Marks an endpoint as a socket-based endpoint.

class ChatService {
@LRPCSocket
async handleMessage(data: any) {
// Handle socket message
}
}
  • target: any - The class prototype.
  • key: string - The method name.

Marks a field in an LRPCPayload as required.

class User {
@LRPCProp
firstName: string;
}
  • target: any - The class prototype.
  • key: string - The field name.

Decorates a field with object-type definitions.

class User {
@LRPCObjectProp({ name: 'string' }, false)
metadata: { name: string };
}
  • value: any - The object type definition.
  • optional: boolean - Whether the property is optional.

Decorates a field with a custom type definition such as enums or unions.

class Task {
@LRPCType(`'start' | 'stop' | 'pause' | 'resume'`, false)
status: 'start' | 'stop' | 'pause' | 'resume';
}
  • value: any - The custom type definition.
  • optional: boolean - Whether the property is optional.

LRPC is optimized for low-latency communication. Benchmarks coming soon.

Feel free to contribute by submitting pull requests or opening issues.

MIT License

For questions, issues, or contributions, contact us at thachromatone@gmail.com