@rettechnologys/nestjs-libs-shared
v1.1.0
Published
Shared library NestJS: helper, type, decorator, filter, interceptor, pipe, validator, dan base entity untuk dipakai lintas service.
Readme
@rettechnologys/nestjs-libs-shared
Shared library NestJS: helper, type, decorator, filter, interceptor, pipe, validator, dan base entity untuk dipakai lintas service.
- Package:
@rettechnologys/nestjs-libs-shared - Repo: https://github.com/rettechnologys/nestjs-libs-shared
- Entry:
dist/index.js· Types:dist/index.d.ts - Target: CommonJS, ES2021, NestJS 10
Daftar Isi
- Cakupan package
- Instalasi
- Setup di project konsumer
- Environment variables
- Common
- Helpers
- Types
- Dukungan versi NestJS
- Development
- Workflow Git
- Publish ke npm
- Troubleshooting
1. Cakupan package
Package ini mengirim 4 folder:
| Ikut dipublish | Isi |
|---|---|
| common/ | decorator, dto, entity, filter, interceptor, interface, pipe, validator |
| helpers/ | fungsi utility murni |
| types/ | enum, interface, constant |
| configs/ | class config class-validator per-domain (JWTConfig, RedisConfig, SentryConfig, AzureADConfig, dst) — lihat catatan secret di bawah |
Tidak ikut dipublish (tetap ada di repo, di-exclude lewat tsconfig.build.json):
| Di-exclude | Alasan |
|---|---|
| src/modules/ | 13 global module (upload, xlsx, pdf, redis pub/sub, SSE, logger, i18n, typeorm) — bawa dependency berat |
Konsekuensinya dependency yang ikut ter-install di konsumer turun drastis — puppeteer, sharp, minio, pdf-lib, ioredis, @nestjs-modules/ioredis, cheerio tidak lagi ikut.
Kalau butuh global module, ambil langsung dari repo — bukan dari npm.
1.1 Catatan secret di configs/
Semua field yang sebelumnya berisi secret/credential hardcoded (JWT signing secret, Sentry auth token, API key OneSignal/WhatsApp/Shortlink/Google Calendar, Redis password, Azure AD client/tenant id) sudah dihapus default value-nya dan jadi field wajib (!). Konsumer wajib mengisi lewat ENV atau constructor/DI sendiri sebelum instance divalidasi — class-validator akan melempar error kalau field itu kosong. Field non-sensitif (URL, timeout, port, flag) tetap punya default aman.
Kalau menambah class config baru di
src/configs/, jangan hardcode secret/credential asli sebagai default value — folder ini ikut ter-publish ke npm public.
2. Instalasi
npm install @rettechnologys/nestjs-libs-sharedPackage ini scoped public, tidak perlu auth untuk install.
Peer dependencies (WAJIB ada di project konsumer)
npm install @nestjs/common@^10 @nestjs/core@^10 @nestjs/platform-express@^10 \
@nestjs/cache-manager@^2.3 @nestjs/jwt@^10.2 @nestjs/microservices@^10 \
@nestjs/swagger@^7.4 typeorm@^0.3.31 rxjs@^7 reflect-metadata@^0.2 \
class-validator@^0.14 class-transformer@^0.5class-validator dan class-transformer sengaja jadi peer (bukan dependency) karena keduanya menyimpan metadata storage global. Kalau ada dua copy di node_modules, decorator validasi terdaftar di storage yang berbeda dari yang dibaca ValidationPipe — validasi diam-diam tidak jalan, tanpa error.
Penting: versi
@nestjs/*harus 10.x. Di npm 7+ peer mismatch bukan warning — install gagal denganERESOLVE. Lihat Dukungan versi NestJS.
3. Setup di project konsumer
tsconfig
Wajib aktif karena library pakai decorator NestJS:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"target": "es2021",
"esModuleInterop": true,
"skipLibCheck": true
}
}Import
Semua export tersedia dari root package (tidak ada subpath export):
import {
AllExceptionFilter,
ResponseFormatInterceptor,
SanitizePipe,
GetCurrentUser,
BaseIE,
LOGGER_SERVICE,
ILoggerService,
isProd,
toNumber,
} from '@rettechnologys/nestjs-libs-shared';Logger port (WAJIB kalau pakai filter/interceptor)
Library ini tidak membawa implementasi logger. Filter dan interceptor bergantung pada port ILoggerService lewat token LOGGER_SERVICE — konsumer yang menyediakan implementasinya.
import {
ILoggerService,
LOGGER_SERVICE,
CACHE_NAMESPACE,
} from '@rettechnologys/nestjs-libs-shared';
import { ConsoleLogger, Global, Injectable, Module } from '@nestjs/common';
@Injectable()
export class AppLogger extends ConsoleLogger implements ILoggerService {
// ConsoleLogger sudah menyediakan setContext(), log(), error(), debug()
getDefaultLang(): string {
return process.env.APP__DEF_LANG ?? 'id';
}
}
@Global()
@Module({
providers: [
{ provide: LOGGER_SERVICE, useClass: AppLogger },
{ provide: CACHE_NAMESPACE, useValue: 'myapp:' },
],
exports: [LOGGER_SERVICE, CACHE_NAMESPACE],
})
export class AppLoggerModule {}getDefaultLang() wajib mengembalikan string, bukan undefined — hasilnya dipakai langsung sebagai lang oleh i18n.
Siapa butuh apa:
| Komponen | Token/dependency yang harus tersedia |
|---|---|
| AllExceptionFilter | LOGGER_SERVICE, HttpAdapterHost (@nestjs/core), I18nService (nestjs-i18n) |
| ResponseFormatInterceptor | LOGGER_SERVICE, ACTIVITY_LOGGER (IActivityLogger) |
| ClearCacheInterceptor | LOGGER_SERVICE, CACHE_NAMESPACE, CACHE_MANAGER (@nestjs/cache-manager) |
| GrpcClientInterceptor | LOGGER_SERVICE, ClsService (nestjs-cls) |
| GrpcClientRetryInterceptor | LOGGER_SERVICE, ClsService (nestjs-cls) |
| ResponseSerialize() / ResponseSerializerInterceptor | logger opsional, lewat argumen ketiga |
| pipe, validator, decorator, helper, entity, dto | tidak butuh apa pun |
Contoh main.ts
import {
AllExceptionFilter,
SanitizePipe,
} from '@rettechnologys/nestjs-libs-shared';
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new SanitizePipe(),
new ValidationPipe({ transform: true, whitelist: true }),
);
// AllExceptionFilter punya dependency DI - resolve lewat container, jangan `new`
app.useGlobalFilters(app.get(AllExceptionFilter));
await app.listen(3000);
}
bootstrap();4. Environment variables
helpers/ dan common/ sengaja tidak bergantung ke class config di configs/ — keduanya membaca process.env langsung, supaya tetap bisa dipakai tanpa setup configs/ sama sekali. Tidak ada validasi schema di jalur ini — kalau variabel tidak diset nilainya undefined, dan fungsi terkait berperilaku sesuai default masing-masing.
| Variable | Dipakai oleh |
|---|---|
| APP__ENV | isProd(), isDev(), isDevLocal(), isStag() — paling sering dibaca |
| APP__FE_URL | getFeBaseUrl(), compileContent() |
| APP__URL, APP__HOST, APP__PATH_PREFIX | helper URL & upload |
| APP__DATE_FORMAT, APP__TIME_FORMAT, APP__FE_DATE_FORMAT | moment.helper |
| CRYPTO__ALGORITHM, CRYPTO__SECRET_KEY, CRYPTO__CIPHER_IV | encryptCrypto(), decryptCrypto() |
| APP__UPLOAD_DIR_PUBLIC, APP__UPLOAD_DIR_PRIVATE, APP__UPLOAD_MAX_SIZE_GLOBAL | upload.helper, SanitizeFilePipe |
| APP__LOGIN_TYPE | getClientType() |
| MINIO__BUCKET_NAME | upload.helper |
| SENTRY__ENABLE | AllExceptionFilter ('true' untuk aktif) |
CRYPTO__*wajib diset kalau memakaiencryptCrypto()/decryptCrypto()— jangan hardcode, pakai secret manager.
getEnvFilePath() masih tersedia di helpers untuk memilih file .env berdasarkan APP__ENV (.env.dev, .env.stag, .env.prod, atau .env kalau tidak diset). File dicari relatif terhadap process.cwd() dan throw EnvFile Not Found kalau tidak ada.
5. Common
Decorators
@Get('me')
findMe(
@GetCurrentUser() user: any,
@GetUserData() userData: any,
@GetUserPermission() permission: any,
@GetIpAddress() ip: string,
@GetUserAgent() ua: string,
@GetDeviceDetector() device: IClientDetails,
) {}Tersedia juga @ApiSwaggerResponse(...) dan decorator text-format untuk transform value DTO.
@GetUserData()dan@GetUserPermission()membacarequest.session.*— pastikan middleware session/auth sudah mengisinya, kalau tidak hasilnyaundefined.
Entities (base class)
Abstract base entity TypeORM untuk diturunkan: BaseIE, Base2IE, IdNameIE, IdNameDescIE, IdName2IE, IdNameDesc2IE, IdTitleIE, IdTitleDescIE, SysLogIE, plus FullNameIE, DatesIE, DatesSoftDelIE dan helper getCreateUpdateBy(), getCreateUpdateDates().
Filters
| Filter | Butuh LOGGER_SERVICE? |
|---|---|
| AllExceptionFilter | Ya (+ HttpAdapterHost, I18nService) |
| HttpExceptionFilter | Tidak |
| GrpcExceptionFilter | Tidak |
Interceptors
| Interceptor | Fungsi | Butuh LOGGER_SERVICE? |
|---|---|---|
| ResponseFormatInterceptor | Bungkus response ke envelope standar | Ya (+ ACTIVITY_LOGGER) |
| ResponseSerializerInterceptor | Serialisasi via class-transformer | Opsional |
| ClearCacheInterceptor | Invalidasi cache setelah mutasi | Ya (+ CACHE_NAMESPACE, CACHE_MANAGER) |
| GrpcClientInterceptor | Propagasi context gRPC | Ya (+ ClsService) |
| GrpcClientRetryInterceptor | Retry call gRPC | Ya (+ ClsService) |
| GrpcServerInterceptor | Sisi server gRPC | Tidak |
ClearCacheInterceptor memakai token CACHE_NAMESPACE (string prefix key yang di-scan). Sebelumnya nilai ini diambil dari RootConfig.redis.ns.
Pipes
SanitizePipe— sanitasi input (XSS) untuk string payload.SanitizeFilePipe— validasi mime type & magic bytes file upload.
Validators (class-validator custom)
export class CreateJobDto {
@IsCronExpression()
cron!: string;
@IsMatch('password')
passwordConfirm!: string;
}Tersedia: cron expression, IsFile, IsInDb, IsMatch, is-date. Nama decorator persisnya lihat src/common/validators/*.validator.ts.
Interfaces
ILoggerService+LOGGER_SERVICE— port logger, lihat Logger port.IActivityLogger+ACTIVITY_LOGGER— port activity log, dipakaiResponseFormatInterceptor.
6. Helpers
Fungsi murni, bisa dipakai di luar konteks Nest.
| File | Contoh fungsi |
|---|---|
| app.helper | isProd(), isDev(), isStag(), getIpAddress(), getDeviceDetails(), getClientType(), getHttpDesc(), getAdminPermissions(), base64Encode/Decode(), randomNumber() |
| cast.helper | toNumber(), toBoolean(), toDate(), trim(), toLowerCase() |
| check.helper | isNil(), isStringFull(), isArrayFull(), isObjectFull(), isJson(), isValidUrl(), isFile() |
| string.helper | capitalCaseStr(), sentenceCaseStr(), limitWordStr(), snakeLowerCase(), kebabLowerCase(), formatPhoneNumber() |
| crypto.helper | encryptCrypto(), decryptCrypto() |
| cron.helper | parseCronExpression(), getCronDescription() |
| object.helper | flattenObject(), getObjValByKeyFromObj(), generateDatasetObj() |
| upload.helper | getAllowedMimeTypes(), pathDocToBase64(), bufferToBase64() |
| notification.helper | compileContent() (handlebars), getDetailContent() |
| env.helper | getEnvFilePath() |
| moment.helper, cookies.helper, typeorm.helper, remove-prop.helper | utilitas tanggal, cookie, query TypeORM, hapus properti |
7. Types
Enum, interface, dan constant: EDBType, ELangCode, ELoginType, EProtocolType, EGender, IClientDetails, IJWTUser, IResponseHttp, IResponseMeta, IParamsUpload, MessageSse, UNKNOWN, dll. Lihat src/types/.
8. Dukungan versi NestJS
Versi Nest yang jalan saat runtime selalu milik project konsumer. Library ini tidak ikut menginstall Nest — peerDependencies cuma kontrak "konsumer wajib punya versi ini". Jadi kalau konsumer naik ke Nest 11, yang perlu diubah adalah range peer di library ini, bukan menyamakan versi runtime.
| Field di package.json | Ikut ter-install di konsumer? | Fungsi |
|---|---|---|
| peerDependencies | Tidak — hanya dicek npm | Kontrak versi yang wajib disediakan konsumer |
| devDependencies | Tidak | Hanya untuk tsc build di repo ini |
| dependencies | Ya | Ikut masuk node_modules konsumer |
Status saat ini: terkunci di Nest 10. Dual-support 10+11 tidak bisa sebagian, karena dua peer ini hard-pin ke satu major:
| Paket | Rentang peer-nya | Bisa dual 10+11? |
|---|---|---|
| @nestjs/jwt 11 | ^8 \|\| ^9 \|\| ^10 \|\| ^11 | Ya |
| @nestjs/cache-manager 3 | ^9 \|\| ^10 \|\| ^11 | Ya, tapi butuh cache-manager >=6 + keyv >=5 |
| @nestjs/swagger 11 | ^11.0.1 | Tidak — wajib Nest 11 |
| @nestjs/microservices 11 | ^11.0.0 | Tidak — wajib Nest 11 |
Kalau mau pindah ke Nest 11 — perlakukan sebagai rilis major:
- Naikkan
peerDependencies+devDependencies@nestjs/*ke^11,@nestjs/swaggerke^11,@nestjs/cache-managerke^3,@nestjs/jwtke^11. - Naikkan
devDependenciesjuga, supayanpm run buildbenar-benar diuji terhadap API 11 — kalau tidak, breaking change baru ketahuan di konsumer. - Migrasi
cache-managerv5 → v6 (pindah ke arsitektur Keyv) — menyentuhClearCacheInterceptor. Ini satu-satunya bagian yang butuh ubah kode. - Rilis
npm version major.
Dependency pihak ketiga sudah aman untuk Nest 11: nestjs-cls (>=10 <12), nestjs-i18n (*), @rettechnologys/crud (tidak peer ke Nest).
9. Development
Setup
git clone https://github.com/rettechnologys/nestjs-libs-shared.git
cd nestjs-libs-shared
npm installBuild
npm run build # tsc -p tsconfig.json -> dist/tsconfig.json adalah base config (dipakai editor/TS server, tidak exclude apa pun selain node_modules/dist, supaya src/configs tetap type-checked di IDE). npm run build pakai tsconfig.build.json yang extend base config dan menambah exclude src/modules + **/*.spec.ts. Mode ketat aktif: strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, isolatedModules.
dist/hasil build lama bisa menyimpanmodules/dari sebelum di-exclude. Sebelum publish, hapus dulu:rm -rf dist && npm run build.
Testing
Ada file *.spec.ts di src/helpers/, tapi belum ada test runner terkonfigurasi di package.json. Untuk menjalankannya, tambahkan Jest + ts-jest beserta script test.
Aturan menambah export baru
Setiap file baru wajib didaftarkan di barrel index.ts terdekat, kalau tidak ia tidak akan ikut ter-export:
src/helpers/foo.helper.ts -> tambah export * from './foo.helper'; di src/helpers/index.ts
src/common/pipes/bar.pipe.ts -> tambah export * from './bar.pipe'; di src/common/pipes/index.tsJangan menambah import dari src/modules ke dalam folder yang dipublish (common/, helpers/, types/, configs/) — build tetap lolos di lokal (file-nya ada di repo) tapi dist/ jadi merujuk file yang tidak pernah ter-emit, dan konsumer kena MODULE_NOT_FOUND. Kalau butuh sesuatu dari sana, bikin port di common/interfaces/ seperti ILoggerService. Import dari src/configs sekarang aman karena ikut ter-publish, tapi helpers//common/ tetap sengaja tidak bergantung ke sana — lihat Environment variables.
Cek dependency
Sebelum publish, pastikan setiap require() di dist/ sudah dideklarasikan:
rm -rf dist && npm run build
grep -rhoE 'require\("[^."][^"]*"\)' dist | sed -E 's/require\("//; s/"\)//' | sort -uImport yang type-only tidak muncul di dist/ — itu masuk devDependencies, bukan dependencies (contoh: cache-manager, keyv, exceljs).
Testing lokal sebelum publish
# Opsi A - tarball (paling mirip hasil publish)
npm run build
npm pack
cd ../project-konsumer
npm install ../nestjs-libs-shared/rettechnologys-nestjs-libs-shared-1.0.0.tgz
# Opsi B - symlink
npm link
cd ../project-konsumer && npm link @rettechnologys/nestjs-libs-shared
npm linkbisa memicu "two copies of reflect-metadata / class-validator". Kalau muncul, pakai Opsi A.
Cek isi tarball:
npm pack --dry-runHarus berisi hanya dist/ (common, helpers, types, index) + README.md + package.json.
10. Workflow Git
Remote: origin → https://github.com/rettechnologys/nestjs-libs-shared.git
First push (repo belum punya commit)
git status # pastikan dist/, node_modules/, .env* tidak muncul
git add .
git commit -m "chore: initial commit"
git push -u origin masterKalau default branch di GitHub masih
main, ubah kemasterlewat Settings → Branches → Default branch setelah push pertama.
Daily workflow
git checkout -b feat/nama-fitur
# ... ubah kode ...
npm run build # pastikan compile bersih
git add -p
git commit -m "feat(validators): tambah IsFile ke barrel export"
git push -u origin feat/nama-fitur
gh pr create --base master --fillFormat commit message
<type>(<scope>): <deskripsi imperatif>Type: feat, fix, refactor, style, chore, ci, perf, docs, test, revert.
Scope = area domain/module, bukan layer:
feat(interceptors): pakai token LOGGER_SERVICE untuk decouple logger
fix(helpers): kembalikan string dari sentenceCaseStr saat input kosong
chore(deps): buang puppeteer dan sharp dari dependenciesAturan branch
master— branch rilis, selalu buildable. Jangan commit langsung.feat/*,fix/*,chore/*— branch kerja, merge lewat PR.- Tag versi (
v1.0.1) dibuat otomatis olehnpm version.
11. Publish ke npm
11.0 Prasyarat (sekali saja)
- Punya akun npm dan jadi member org
rettechnologysdengan hak publish. Kalau org belum ada, buat di https://www.npmjs.com/org/create (org gratis boleh publish package public). - Login:
npm login
npm whoami # harus keluar username, bukan error 401- Pastikan registry benar:
npm config get registry # https://registry.npmjs.org/
npm config get @rettechnologys:registry # null / registry npm"publishConfig": { "access": "public" } sudah ada di package.json — itu yang mencegah scoped package dianggap private/berbayar.
11.1 Checklist sebelum publish
rm -rf dist && npm run build # harus 0 error, dan dist bersih dari build lama
npm pack --dry-run # cek isi tarball
git status # working tree bersih
git pull origin masterCek juga:
- [ ]
versionbelum pernah dipublish (npm tidak bisa publish versi sama dua kali, bahkan setelah unpublish). - [ ]
dist/tidak mengandungmodules/. - [ ]
dist/mengandungconfigs/(ikut dipublish by design). - [ ] Tidak ada secret/kredensial di
dist/(termasuk diconfigs/— semua field sensitif harus tanpa default, lihat 1.1). - [ ] Breaking change → naikkan major.
11.2 Naikkan versi
npm version patch # 1.0.0 -> 1.0.1 (bug fix, API tetap)
npm version minor # 1.0.0 -> 1.1.0 (fitur baru, backward compatible)
npm version major # 1.0.0 -> 2.0.0 (breaking change)| Perubahan | Bump | |---|---| | Fix bug tanpa ubah signature | patch | | Tambah helper / validator / type baru | minor | | Rename atau hapus export, ubah token DI, naikkan peer NestJS | major |
Penyempitan cakupan ke
common+helpers+typesdan pindah ke tokenLOGGER_SERVICEkeduanya breaking — rilis berikutnya harusmajor.
11.3 Publish
npm publishprepublishOnly otomatis menjalankan npm run build. Kalau build gagal, publish dibatalkan. Dengan 2FA: npm publish --otp=123456.
11.4 Push commit + tag
git push origin master --follow-tags--follow-tags mengirim commit dan tag dari npm version. Tanpa flag ini tag hanya ada di lokal.
11.5 Verifikasi
npm view @rettechnologys/nestjs-libs-shared version
npm view @rettechnologys/nestjs-libs-shared versions --json11.6 Alur rilis ringkas
git checkout master && git pull
rm -rf dist && npm run build && npm pack --dry-run
npm version major
npm publish
git push origin master --follow-tags11.7 Pre-release (opsional)
npm version prerelease --preid=beta # 1.0.1 -> 1.0.2-beta.0
npm publish --tag betaKonsumer: npm install @rettechnologys/nestjs-libs-shared@beta.
11.8 Kalau salah publish
- Dalam 72 jam:
npm unpublish @rettechnologys/[email protected]. Versi yang di-unpublish tidak bisa dipakai ulang. - Lewat 72 jam: tidak bisa unpublish, pakai deprecate + rilis perbaikan:
npm deprecate @rettechnologys/[email protected] "Rusak, pakai 1.0.2"- Secret ikut ter-publish: rotasi secret-nya sekarang juga. Tarball yang sudah tersebar tidak bisa ditarik.
12. Troubleshooting
| Gejala | Penyebab & solusi |
|---|---|
| Nest can't resolve dependencies of AllExceptionFilter (?, ...) | Token LOGGER_SERVICE belum diprovide. Lihat Logger port. |
| Nest can't resolve dependencies of ClearCacheInterceptor | Kurang CACHE_NAMESPACE atau CACHE_MANAGER. |
| Cannot find module '../../modules' saat runtime | Ada file di common/helpers/types/configs yang mengimport src/modules (folder yang di-exclude). Bikin port di common/interfaces/, jangan import langsung. |
| GlobalUploadService tidak ada lagi | Memang sudah tidak dipublish (ada di src/modules) — lihat Cakupan package. Ambil dari repo. |
| Class config di configs/ melempar validation error field kosong | By design — field sensitif (secret, API key, password) tidak punya default. Isi lewat ENV/DI sendiri, lihat 1.1. |
| npm error 401 Unauthorized saat publish | Belum npm login atau tidak punya akses org. Cek npm whoami. |
| npm error 402 Payment Required | Scoped package dianggap private. Pastikan publishConfig.access = "public", atau npm publish --access public. |
| npm error 403 You cannot publish over the previously published versions | Versi sudah ada. npm version patch dulu. |
| ERESOLVE unable to resolve dependency tree saat install | Versi @nestjs/* di konsumer bukan 10.x. Turunkan ke Nest 10, atau sementara npm install --legacy-peer-deps. |
| Reflect.getMetadata is not a function | reflect-metadata belum di-import di main.ts konsumer, atau emitDecoratorMetadata mati di tsconfig. |
| ERR_REQUIRE_ESM saat import package | Ada dependency ESM-only yang masuk. Library ini compile ke CommonJS — jangan tambah dependency "type": "module" yang tidak punya kondisi require di exports. |
| Import baru tidak muncul di package | Belum didaftarkan di barrel index.ts. Lihat Aturan menambah export baru. |
| Validasi class-validator tidak jalan | Ada dua copy class-validator. Pastikan cuma satu (peer, bukan dependency). |
| EnvFile Not Found: /path/.env.dev | APP__ENV diset tapi file .env.<env> tidak ada di process.cwd(). |
Lisensi
Internal — RET Technologys.
