Write NestJS-like decorators. Get C-level serialisation performance.
@Dto()
class CreateUserDto {
@Field('string', 32) firstName!: string;
@Field('string', 32) lastName!: string;
@Field('number') age!: number;
@Field('string', 32) email!: string;
@Field('string', 1) sex!: string;
@Field('string', 32) password!: string;
@Field('boolean') active!: boolean;
}JSON serialisation allocates memory on every request — JSON.parse creates objects,
JSON.stringify creates strings. Under high load, GC pauses eat your latency budget.
@layra/rpc inverts the model:
- You declare your DTO with decorators (no manual offset calculations)
- Code generator produces monomorphic read/write functions with compile-time constants
- DtoPool pre-creates all DTO+Buffer pairs at startup — zero allocations on hot path
- Getters read directly from the binary buffer — no object creation per request
npm install @layra/rpcOr directly from GitHub:
{
"dependencies": {
"@layra/rpc": "dualitysol/layra-rpc"
}
}// user.dto.ts
import { Dto, Field } from '@layra/rpc';
@Dto()
class CreateUserDto {
@Field('string', 32) firstName!: string;
@Field('string', 32) lastName!: string;
@Field('number') age!: number;
@Field('string', 32) email!: string;
@Field('string', 32) password!: string;
@Field('boolean') active!: boolean;
}
@Dto()
class UserCreatedDto {
@Field('boolean') success!: boolean;
@Field('string', 128) message!: string;
}npx layra-compile src/user.dto.ts src/generated/user.dto.tsThis produces:
// Generated: SIZE, OFFSET_XXX, readXxx(), writeXxx(), createDTO(), writeToBuffer()
export const SIZE = 167; // total buffer size
export const OFF_FIRSTNAME = 0; // byte offsets — auto-calculated
export const OFF_LASTNAME = 33;
// ...
// Monomorphic — V8 inlines these
export function readFirstName(buf: Buffer): string { ... }
export function writeEmail(buf: Buffer, value: string): void { ... }
// Shared prototype — zero alloc per instance
export function createDTO(buf: Buffer): CreateUserDtoView { ... }import { DtoPool } from '@layra/rpc';
import { createDTO, SIZE } from './generated/user.dto';
const pool = new DtoPool(createDTO, SIZE, 50000);
// Hot path — every request:
const { dto, buf } = pool.acquire();
// stream request body into `buf`
const email = dto.email; // ← getter reads from buf, zero alloc!
pool.release({ dto, buf });import { Dto, Field, Method, UseCase } from '@layra/rpc';
@Method({ name: 'CreateUser', payload: CreateUserDto, response: UserCreatedDto })
class CreateUserUseCase extends UseCase {
handle(dto: CreateUserDto): UserCreatedDto {
return { success: true, message: dto.email };
}
}Tested on Node.js 24 with autocannon (100 connections, 15s):
| Metric | @layra/rpc (binary) | Fastify (JSON) | Improvement |
|---|---|---|---|
| RPS (avg) | 56,613 | 38,534 | +47% |
| Latency (avg) | 1.16 ms | 2.22 ms | -48% |
| Latency (p99) | 4.00 ms | 8.00 ms | -50% |
| Throughput | 15.87 MB/s | 8.27 MB/s | +92% |
| Decorator | Description |
|---|---|
@Dto() |
Marks a class as a DTO |
@Field(type, maxLen?) |
Declares a field with type |
@Method(opts) |
Registers a use case handler |
| Export | Description |
|---|---|
DtoPool |
Zero-allocation pool of pre-created DTOs |
BufferPool |
Generic buffer pool |
FixedBufferPool |
Same-size buffer pool |
createServer() |
Create a TCP RPC server |
| Export / CLI | Description |
|---|---|
generateBinaryView(cls) |
Generate TS code from a DTO class |
layra-compile <input> |
CLI wrapper around the generator |
import { ... } from '@layra/rpc'; // Main — decorators, runtime, pool
import { generateBinaryView } from '@layra/rpc/codegen'; // Code generator
import { DtoPool } from '@layra/rpc/runtime'; // Runtime only
import { createServer } from '@layra/rpc/server'; // TCP serverISC © Artem Tantsura