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.
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");
@LRPCAuth
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.@LRPCPayload
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.@LRPCPropOp
Marks a field in an LRPCPayload
as optional.
class User {
@LRPCPropOp
middleName?: string;
}
target: any
- The class prototype.key: string
- The field name.@LRPCSocket
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.@LRPCProp
Marks a field in an LRPCPayload
as required.
class User {
@LRPCProp
firstName: string;
}
target: any
- The class prototype.key: string
- The field name.@LRPCObjectProp
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.@LRPCType
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