@mondart/nestjs-common-module-core
v3.2.8
Published
Core utilities, DTOs, decorators, entities, filters, validators and helpers for NestJS.
Readme
@mondart/nestjs-common-module-core
Foundational package for the @mondart/nestjs-common-module-* family: the
global exception filter, base TypeORM entities, the standard success/error
response envelopes, request DTOs, i18n-aware validation decorators, and a
handful of request-context helpers (IamContext, GetUser, GetAgent) that
the rest of the workspace's packages build on. Most other @mondart packages
depend on this one.
Global exception handling
Register GlobalExceptionFilter together with nestjs-i18n's validation
pipe during bootstrap:
import {
GlobalExceptionFilter,
validationOptions,
} from '@mondart/nestjs-common-module-core';
import { I18nValidationPipe } from 'nestjs-i18n';
app.useGlobalPipes(new I18nValidationPipe(validationOptions));
app.useGlobalFilters(new GlobalExceptionFilter(true)); // true = report to SentryGlobalExceptionFilter catches everything — Nest's built-in exceptions,
package-local exceptions (e.g. TooManyRequestsException), plain errors, and
I18nValidationException — and always responds with the same ErrorResponse
envelope (name, message, status, details), for both HTTP and RPC
(Kafka) contexts. See the root README.md for the full i18n validation
translation setup (VALIDATION_FAILED / VALIDATION_FAILED_DESCRIPTION
translation keys); this package only pins the nestjs-i18n version and
supplies the filter and pipe options.
Base entities
BaseModelEntity is the TypeORM base every entity in a consuming service
should extend, directly or through one of the composed variants:
import { Entity, Column } from 'typeorm';
import { BaseModelWithAdminAndUserActionsEntity } from '@mondart/nestjs-common-module-core';
@Entity()
export class Product extends BaseModelWithAdminAndUserActionsEntity {
@Column()
name: string;
}BaseModelEntity— auto-incrementid.BaseModelWithDatesEntity— addscreatedAt/updatedAt/deletedAt/metadata.BaseModelWithAdminActionsEntity/BaseModelWithUserActionsEntity/BaseModelWithAdminAndUserActionsEntity— addcreatedBy*/updatedBy*/deletedBy*audit columns on top ofBaseModelWithDatesEntity.
Columns declared on these base classes are ordered last (via the @Order
decorator) so that a subclass's own columns come first in generated SQL and
Swagger schemas.
Response and request envelopes
SuccessResponse is the standard success envelope. Pass an nestjs-i18n
translation key (message.SOME_KEY or validation.SOME_KEY, optionally
|{"arg":"value"} for interpolation) as the message and it is translated
against the current I18nContext automatically; anything else is used as a
literal message:
import { SuccessResponse } from '@mondart/nestjs-common-module-core';
return new SuccessResponse(product, 'message.PRODUCT_CREATED');ErrorResponse is what GlobalExceptionFilter emits, and is also usable
directly:
import { ErrorResponse } from '@mondart/nestjs-common-module-core';
new ErrorResponse({
name: 'NotFoundException',
message: 'Not found',
status: 404,
});Common request DTOs — IdDto, IdsDto (comma-separated ids query param),
UUIDDto, BaseRequest (isActive / metadata) — cover the recurring
"find/act by id(s)" and soft-flag shapes so controllers don't redeclare them.
Validation decorators
Every class-validator decorator re-exported from this package
(IsString, IsNumber, IsEnum, Min, MaxLength, ...) is a thin wrapper
around the real one that swaps the default message for a ValidationMessageKey
translation key, so validation errors come back already i18n-ready once they
pass through GlobalExceptionFilter. Use them exactly like their
class-validator counterparts:
import { IsString, IsNotEmpty } from '@mondart/nestjs-common-module-core';
export class CreateProductDto {
@IsString()
@IsNotEmpty()
name: string;
}A few composite decorators cover common cross-field rules:
import {
IsId,
IsOnlyOneOf,
DependOnProperty,
IsDateRangeValid,
} from '@mondart/nestjs-common-module-core';
class Query {
@IsId({ optional: true }) // int, positive, <= max safe int, optional
storeId?: number;
@IsOnlyOneOf('email') // exactly one of `phone`/`email` must be set
phone?: string;
@DependOnProperty('bank', ['iban', 'shebaNumber']) // both required when method === 'bank'
method: string;
@IsDateRangeValid('current', '>=') // from >= today
from: Date;
}IsUnique and DoesExist are async, @InjectDataSource()-backed
class-validator constraints for checking a value against a TypeORM
repository (uniqueness / existence) directly from a DTO:
import { Validate } from 'class-validator';
import { IsUnique, DoesExist } from '@mondart/nestjs-common-module-core';
class CreateUserDto {
@Validate(IsUnique, [{ repository: 'User' }])
email: string;
@Validate(DoesExist, ['Store'])
storeId: number;
}Request-context decorators and the auth throttle guard
IamContext is the canonical way to read the identity injected by upstream
auth (via the injectedpayload header) inside a controller:
import { IamContext, IamContextDto } from '@mondart/nestjs-common-module-core';
@Get()
findAll(@IamContext() iam: IamContextDto) {
// iam.userId / iam.agentId / iam.isAgent / iam.ip / iam.requestId
}GetUser and GetAgent expose the same underlying data as plain param
decorators when only the user or agent half is needed. AuthThrottlerGuard
extends @nestjs/throttler's ThrottlerGuard to key rate limits off the IAM
context (by IP, hashed email, or hashed device id depending on the configured
throttler name) instead of the default IP-only tracker, and throws
TooManyRequestsException (translated by GlobalExceptionFilter) once a
limit is hit.
Swagger response decorator
CustomApiResponse documents an endpoint's response as SuccessResponse
wrapping a given DTO, instead of hand-writing the allOf/$ref schema:
import { CustomApiResponse } from '@mondart/nestjs-common-module-core';
@CustomApiResponse({ options: { status: 200, description: 'OK', responseDto: ProductDto, isArray: true } })
@Get()
findAll() { ... }TypeORM strategies
SnakeNamingStrategy (snake_case columns/tables) and TypeOrmLoggerStrategy
(routes query logs through Nest's Logger and forwards errors/slow
queries/warnings to Sentry) are meant to be wired into TypeOrmModule:
import { SnakeNamingStrategy, TypeOrmLoggerStrategy } from '@mondart/nestjs-common-module-core';
TypeOrmModule.forRoot({
namingStrategy: new SnakeNamingStrategy(),
logger: new TypeOrmLoggerStrategy(),
...
});Helpers
A grab bag of stateless static-method helpers is exported for common needs:
HmacHelper.hmac / AesHelper.encrypt/decrypt for hashing and symmetric
encryption, ConvertStringCaseHelper for case conversions, MessageFormatter
for {0}-style SharedMessages interpolation, EnvValidator for validating
ConfigModule config against class-validator-decorated schema classes, and
RxjsCatchErrorHelper / RxjsCustomTimeoutHelper RxJS operators for mapping
errors/timeouts on outbound calls to Nest exceptions.
Graceful HTTP shutdown
Use GracefulShutdownHelper.enable() instead of app.enableShutdownHooks()
in an HTTP service bootstrap:
import { GracefulShutdownHelper } from '@mondart/nestjs-common-module-core';
const app = await NestFactory.create(AppModule);
GracefulShutdownHelper.enable(app);
await app.listen(appPort);On SIGTERM or SIGINT, the helper rejects new requests with 503 and a
Connection: close header, closes idle keep-alive connections, waits up to
110 seconds for active responses, and then calls app.close() so Nest module,
Kafka, queue, and database shutdown hooks run. Configure the container stop
grace period above that deadline (the Swarm deployment uses two minutes).
Do not register Nest's default shutdown signal handlers alongside this helper.
During app.close(), the helper suppresses only ioredis's known
Connection is closed. rejection caused by an already-closing Bull connection;
any other unhandled shutdown rejection is logged and produces a failed exit.
Kafka-related exports
BaseKafkaEventDto, KafkaRequestDto, KafkaSuccessResponse,
KafkaFailedResponseDto, KafkaLanguageResolver, and the RxJS helpers above
remain exported here for backward compatibility. New code should import them
from @mondart/nestjs-common-module-kafka instead — see the root
README.md for the current Kafka language-propagation setup.
