@devoven/common
v0.0.2
Published
Shared types and utilities for the NestJS monorepo
Downloads
18
Readme
@devoven/common
Shared types and utilities for NestJS applications. Not a NestJS module — exports individual components you import directly.
Installation
npm install @devoven/common
# or
pnpm add @devoven/commonPeer dependencies
Requires the standard NestJS peer dependencies (@nestjs/common, @nestjs/core), which any NestJS application already has. No additional peer deps are needed.
No module registration is required. Import each export directly where you need it.
PaginatedResult
A value object for paginated list responses.
import { PaginatedResult } from '@devoven/common';
const result = PaginatedResult.create(items, total, page, pageSize);
// or an empty page:
const empty = PaginatedResult.empty(1, 20);| Property / Method | Type | Description |
|-------------------|------|-------------|
| data | T[] | Items on the current page |
| total | number | Total number of items across all pages |
| page | number | Current page (1-based) |
| pageSize | number | Maximum items per page |
| totalPages | number (computed) | Math.ceil(total / pageSize) |
| hasNextPage | boolean (computed) | page < totalPages |
| hasPreviousPage | boolean (computed) | page > 1 |
| PaginatedResult.create(data, total, page, pageSize) | static | Validates inputs and returns an instance |
| PaginatedResult.empty(page?, pageSize?) | static | Returns an empty result (defaults: page 1, pageSize 20) |
create throws if page < 1, pageSize < 1, or total < 0.
PaginationOptions
An interface for query parameters passed to list endpoints or repository methods.
import { PaginationOptions, ESortOrder } from '@devoven/common';
const opts: PaginationOptions = {
page: 2,
pageSize: 10,
sortBy: 'createdAt',
sortOrder: ESortOrder.DESC,
};| Field | Type | Description |
|-------|------|-------------|
| page | number (optional) | Page number (1-based) |
| pageSize | number (optional) | Items per page |
| sortBy | string (optional) | Field name to sort by |
| sortOrder | ESortOrder (optional) | ESortOrder.ASC ('asc') or ESortOrder.DESC ('desc') |
TransactionPort
An abstract interface for executing code inside a transaction. Use it as the port your use cases depend on.
import type { TransactionPort } from '@devoven/common';
import { TOKENS } from '@devoven/common';
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class CreateOrderUseCase {
constructor(
@Inject(TOKENS.TransactionPort)
private readonly tx: TransactionPort,
) {}
async execute(): Promise<void> {
await this.tx.execute(async () => {
// all operations here run in one transaction
});
}
}| Method | Signature | Description |
|--------|-----------|-------------|
| execute | (fn: () => Promise<T>, options?: TransactionOptions) => Promise<T> | Run the function inside a transaction |
TransactionOptions has a single optional field: timeout?: number (milliseconds).
DI token: TOKENS.TransactionPort (a Symbol).
TransactionalClientPort
A lower-level interface for ORM clients that support internal $transaction calls. Used by PrismaTransaction to create a nested transaction context.
export interface TransactionalClientPort {
$transaction<TClient, TResult>(
fn: (client: TransactionalClientPort & TClient) => Promise<TResult>,
options?: TransactionOptions,
): Promise<TResult>;
}PrismaTransaction
A TransactionPort implementation backed by Prisma. Uses AsyncLocalStorage to propagate the transactional Prisma client through the call stack, so nested execute calls reuse the existing transaction instead of opening a new one.
Rollback: Prisma's $transaction automatically rolls back all operations if the callback throws. No explicit rollback call is needed — simply let the error propagate out of the execute callback.
import { PrismaTransaction } from '@devoven/common';
import { TOKENS } from '@devoven/common';
// In your app module:
{
provide: TOKENS.TransactionPort,
useFactory: (prisma: PrismaService) => new PrismaTransaction(prisma),
inject: [PrismaService],
}Retrieve the active client (transactional or root) via prismaTransaction.getClient() in your repository:
@Injectable()
export class PrismaOrderRepository implements OrderRepositoryPort {
constructor(
private readonly prisma: PrismaService,
private readonly tx: PrismaTransaction,
) {}
async save(order: Order): Promise<void> {
const client = this.tx.getClient(); // returns tx client if inside a transaction
await client.order.create({ /* ... */ });
}
}NoopTransaction
A TransactionPort implementation that executes the callback directly without any wrapping transaction. Useful in tests or when your storage does not support transactions.
import { NoopTransaction } from '@devoven/common';
import { TOKENS } from '@devoven/common';
{
provide: TOKENS.TransactionPort,
useClass: NoopTransaction,
}HttpExceptionFilter
A global ExceptionFilter that normalises all thrown exceptions into a consistent JSON error shape.
import { HttpExceptionFilter } from '@devoven/common';
// Register globally in main.ts
app.useGlobalFilters(new HttpExceptionFilter());
// Enable debug mode (logs method, path, and timestamp in the response body)
app.useGlobalFilters(new HttpExceptionFilter({ debug: true }));Response shape:
{
"statusCode": 404,
"message": "Role \"editor\" not found",
"error": "Not Found"
}With debug: true an additional debug field is included:
{
"statusCode": 404,
"message": "Role \"editor\" not found",
"error": "Not Found",
"debug": {
"method": "GET",
"path": "/roles/editor",
"timestamp": "2025-01-15T10:30:00.000Z"
}
}Non-HttpException errors produce a 500 response with message "Internal server error". HttpExceptionFilter logs 5xx errors at the error level and 4xx errors at the warn level.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| debug | boolean | false | Include request context in error responses and log the raw exception |
Architecture
@devoven/common contains only framework-agnostic value objects, port interfaces, and thin infrastructure adapters. It has no NestJS module, no controllers, and no DI wiring — consumers wire everything themselves.
Exports at a Glance
| Export | Category |
|--------|----------|
| PaginatedResult<T> | Domain value object |
| PaginationOptions, ESortOrder | Domain types |
| TransactionPort, TransactionalClientPort | Application port interfaces |
| TransactionOptions | Application type |
| PrismaTransaction<T> | Infrastructure adapter |
| NoopTransaction | Infrastructure adapter (testing) |
| HttpExceptionFilter, HttpExceptionFilterOptions | Presentation filter |
| TOKENS | DI token constants |
