@e12e/ts-omni
v2.0.3
Published
TypeScript-first core library for any application — request/response models, validation, type-safe i18n, and DTO transformation.
Maintainers
Readme
@e12e/ts-omni
TypeScript-first core library for any application — request/response models, validation, type-safe i18n, and DTO transformation.
Installation
pnpm add @e12e/ts-omniPeer dependencies (must be installed in your project):
pnpm add class-transformer class-validatorQuick Start
// Import specific modules (tree-shaking friendly)
import { validateObject, validateObjectSync } from "@e12e/ts-omni/validation";
import { ApiResponse } from "@e12e/ts-omni/response";
import { PaginatedQueryDto } from "@e12e/ts-omni/request/dto";TypeScript Config
Enable decorators in your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Required for
class-validator,class-transformer, and custom decorators like@Match,@IsValidFilter.
Modules
| Module | Import Path | Description |
|--------|-------------|-------------|
| Request | @e12e/ts-omni/request | Query types, sort, search, filter |
| Request DTOs | @e12e/ts-omni/request/dto | Validated DTO classes |
| Response | @e12e/ts-omni/response | ApiResponse wrapper (framework-agnostic) |
| Response/Express | @e12e/ts-omni/response/express | Express adapter for ApiResponse |
| Validation | @e12e/ts-omni/validation | Validators & custom decorators |
| i18n | @e12e/ts-omni/i18n | Type-safe translations |
| Util | @e12e/ts-omni/util | DTO transformation helper |
Request
Standardized query interfaces and DTOs for CRUD operations.
Types
import type {
BaseQuery,
PaginatedQuery,
SearchParams,
SortParams,
ObjectFilter,
FindOptions,
FindByIdOptions,
} from "@e12e/ts-omni/request";Paginated Query
interface User {
id: number;
name: string;
email: string;
status: "active" | "inactive";
}
const query: PaginatedQuery<User> = {
page: 1,
limit: 20,
search: { keyword: "john", fields: ["name", "email"] },
filter: { status: { eq: "active" } },
sort: [{ field: "name", order: "asc" }],
};Object Filter
Type-safe filtering with logical operators:
const filter: ObjectFilter<User> = {
status: { eq: "active" },
name: { contains: "john" },
age: { gte: 18 },
OR: [
{ name: { startsWith: "J" } },
{ name: { startsWith: "j" } },
],
};Operators:
- Single:
eq,neq,gt,gte,lt,lte,contains,notContains,startsWith,endsWith,matches,isNull,isNotNull,isEmpty,isNotEmpty - Range:
between,notBetween - List:
in,notIn,hasAny,hasAll,hasNone
Helpers
import { withTiebreak, resolvePageSize, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from "@e12e/ts-omni/request";
// Add tiebreak for deterministic sorting
withTiebreak([{ field: "name", order: "asc" }]);
// → [{ field: "name", order: "asc" }, { field: "id", order: "asc" }]
// Resolve page size with limits
resolvePageSize({ page: 1, limit: 200 }); // → 100 (capped)
// Custom page size options
resolvePageSize({ page: 1, limit: 200 }, { maxPageSize: 50 }); // → 50Request DTOs
Validated DTO classes with class-validator decorators.
import { validateObject } from "@e12e/ts-omni/validation";
import { PaginatedQueryDto, SearchParamsDto, SortParamsDto } from "@e12e/ts-omni/request/dto";
// Async version
const { success, data, errors } = await validateObject(PaginatedQueryDto, {
page: 1,
limit: 20,
search: { keyword: "john", fields: ["name"] },
sort: [{ field: "name", order: "asc" }],
});
// Or sync version (no await needed)
const { success: syncSuccess, data: syncData } = validateObjectSync(PaginatedQueryDto, {
page: 1,
limit: 20,
search: { keyword: "john", fields: ["name"] },
sort: [{ field: "name", order: "asc" }],
});
if (!success) {
console.log(errors);
// [{ field: "page", errors: { is_int: "page must be an integer number" } }]
}Response
ApiResponse — Standardized model for API responses. Framework-agnostic by default, with Express integration available via @e12e/ts-omni/response/express.
import { ApiResponse } from "@e12e/ts-omni/response";JSON Response
// Basic
return ApiResponse.json({ success: true, data: user });
// With DTO transformation
return ApiResponse.json(
{ success: true, data: newUser },
{ status: 201, resDTO: UserResponseDto }
);
// With cookies
return ApiResponse.json(
{ success: true, data: { accessToken: "sk_abc123" } },
{
status: 200,
cookies: [{
name: "refresh_token",
value: "rt_xyz789",
options: { httpOnly: true, secure: true, maxAge: 7 * 24 * 60 * 60 * 1000 },
}],
}
);Redirect & HTML
return ApiResponse.redirect("https://example.com/new", { status: 301 });
return ApiResponse.html("<h1>Welcome!</h1>");Fluent Metadata
return ApiResponse.json({ success: true, data: products })
.setMetadata({ total: "150", page: "1" })
.setMetadata({ traceId: "abc-123" });Express Integration
If you're using Express, import sendToExpress from the Express adapter:
import { sendToExpress } from "@e12e/ts-omni/response/express";
const response = ApiResponse.json({ success: true, data: user });
sendToExpress(response, res);Note:
expressis an optional peer dependency. Only install it if you use the Express adapter.
Types
| Type | Description |
|------|-------------|
| JsonResponse<T> | { success?, status?, message?, data?, error?, metadata? } |
| ResponseError | { message?, type?, fields? } |
| ResponseInit | { status?, headers?, cookies? } |
| Cookie | { name, value, options: CookieOptions } |
| CookieOptions | { path?, domain?, maxAge?, secure?, httpOnly?, sameSite? } |
| PaginatedResponse<T> | { items, page, limit, total } |
Validation
Utilities built on class-validator and class-transformer.
validateObject
import { validateObject } from "@e12e/ts-omni/validation";
class LoginDTO {
@IsEmail()
email: string;
@IsString()
@MinLength(6)
password: string;
}
const { success, data, errors } = await validateObject(LoginDTO, {
email: "invalid",
});validateObjectSync
Synchronous version — same API, no await needed. Uses validateSync from class-validator.
import { validateObjectSync } from "@e12e/ts-omni/validation";
const { success, data, errors } = validateObjectSync(LoginDTO, {
email: "invalid",
});Note: Only use
validateObjectSyncwhen all validators are synchronous. If your DTOs use async validators, stick withvalidateObject.
Custom Decorators
@Match / @NotMatch
import { Match, NotMatch } from "@e12e/ts-omni/validation";
class RegisterDTO {
@IsString()
@MinLength(6)
password: string;
@Match("password")
confirmPassword: string;
}
class ChangeEmailDTO {
@IsEmail()
oldEmail: string;
@IsEmail()
@NotMatch("oldEmail")
newEmail: string;
}@MatchesRegex
import { MatchesRegex } from "@e12e/ts-omni/validation";
const PHONE_REGEX = /^\+?[1-9]\d{1,14}$/;
class UserDTO {
@MatchesRegex(PHONE_REGEX, "invalid_phone_format")
phone: string;
}@IsValidFilter
import { IsValidFilter } from "@e12e/ts-omni/validation";
const filterConfig = {
fields: {
status: { operators: ["eq", "neq"] },
name: { operators: ["contains", "startsWith"] },
},
maxDepth: 3,
maxConditions: 5,
};
class UserQueryDto {
@IsValidFilter(filterConfig)
filter: Record<string, any>;
}validateObjectFilter
import { validateObjectFilter } from "@e12e/ts-omni/validation";
const result = validateObjectFilter(
{ status: { eq: "active" } },
{ fields: { status: { operators: ["eq", "neq"] } } }
);
// { valid: true, errors: [] }i18n
Type-safe translation system with dot-notation keys.
import { initTranslations, TranslationSchema } from "@e12e/ts-omni/i18n";
// 1. Define default locale
const en = {
common: { ok: "OK" },
auth: {
login: {
title: "Sign in",
welcome: "Welcome, {name}!",
},
},
};
type Schema = typeof en;
// 2. Define other locales
const vi: TranslationSchema<Schema> = {
common: { ok: "Đồng ý" },
auth: {
login: {
title: "Đăng nhập",
welcome: "Chào mừng, {name}!",
},
},
};
// 3. Initialize
const { getTranslations } = initTranslations<Schema, ["en", "vi"]>({
messages: { en, vi },
});
// 4. Use
const t = getTranslations("en");
t("common.ok"); // "OK"
t("auth.login.welcome", { name: "Eddie" }); // "Welcome, Eddie!"Features: Type-safe keys, string interpolation, missing key warnings, max 8 levels depth.
Util
transformObject
Wraps class-transformer's plainToInstance with safe defaults.
import { transformObject } from "@e12e/ts-omni/util";
import { Expose } from "class-transformer";
class UserResponseDto {
@Expose()
id: number;
@Expose()
fullName: string;
}
const raw = { id: 1, fullName: "John", password: "secret" };
transformObject(UserResponseDto, raw);
// { id: 1, fullName: "John" } ← password excludedDefaults: excludeExtraneousValues: true, enableImplicitConversion: true
Peer Dependencies
| Package | Version | Required |
|---------|---------|----------|
| class-transformer | ^0.5.1 | Yes |
| class-validator | ^0.15.1 | Yes |
| express | ^4.0.0 || ^5.0.0 | Optional (only for Express adapter) |
License
MIT
