FAQ
General
Do nestarc packages depend on each other?
No. Every package can be installed and used independently. They compose well together via Prisma extension chaining, but it is not required.
Which NestJS versions are supported?
NestJS 10 and 11. Both are tested in CI.
Which Prisma versions are supported?
Support is package-specific. tenancy supports Prisma 6/7; soft-delete, audit-log, and pagination support 5/6/7; feature-flag 0.5 requires Prisma 7. Prisma 7 is the primary development target for all five. See the compatibility matrix.
Do you support both Express and Fastify?
@nestarc/safe-response supports both Express and Fastify out of the box. The other packages are HTTP adapter agnostic.
@nestarc/tenancy
RLS does not seem to be working
Most common causes:
- Connected as superuser — PostgreSQL superusers bypass RLS. Create a dedicated
app_userrole instead. - Missing FORCE ROW LEVEL SECURITY — Without it, table owners also bypass RLS. Run
ALTER TABLE ... FORCE ROW LEVEL SECURITY. - Missing tenant_id column — The table must have a
tenant_idcolumn, and thecurrent_settingkey in the RLS policy must match the extension configuration.
Run npx @nestarc/tenancy check to detect drift between your Prisma schema and SQL policies.
set_config does not work inside interactive transactions
By default, the Prisma extension uses batch transactions internally. set_config does not propagate into interactive transactions ($transaction(async (tx) => ...)).
Two solutions:
- Use the
tenancyTransaction()helper (recommended, works with all Prisma versions) - Enable
interactiveTransactionSupport: true(depends on Prisma internals)
See Installation for details.
How do I skip RLS for specific models?
Use the sharedModels option:
createPrismaTenancyExtension(tenancyService, {
sharedModels: ['Country', 'Currency'],
})Queries on shared models skip set_config and autoInjectTenantId.
How do I query without a tenant context?
Use withoutTenant() to explicitly clear the tenant context. Note that with RLS enabled, queries will return 0 rows. To query across all tenants, you need a separate admin connection that bypasses RLS.
@nestarc/safe-response
How do I disable response wrapping for a specific route?
Use the @RawResponse() decorator:
@Get('health')
@RawResponse()
healthCheck() {
return { status: 'ok' };
}Useful for file downloads, SSE, and health checks.
Is it compatible with class-transformer's @Exclude()?
Yes. Import SafeResponseModule before registering ClassSerializerInterceptor. Serialization runs first, then the result is wrapped.
@nestarc/soft-delete
Unique constraint conflicts after soft-delete
A normal unique constraint still includes soft-deleted rows, while @@unique([email, deletedAt]) can allow duplicate active values on databases that treat NULL values as distinct. Use a PostgreSQL/SQLite partial unique index or a MySQL generated-column index instead. See Active-Row Unique Constraints.
What is the maximum cascade depth?
Default is 3. Adjust with the maxCascadeDepth option.
@nestarc/feature-flag
What is the flag evaluation priority?
Flags are evaluated through a four-layer cascade. The first matching layer wins:
- Archived (always false)
- Best matching attribute override
- Percentage rollout
- Global default
How does percentage rollout work?
It hashes flagKey + targetingKey with murmurhash3 and takes the result modulo 100. The targeting key resolves from an explicit key or configured context (with the service-path constraints documented in the rollout guide), so the same stable identifier remains in the same bucket.
@nestarc/audit-log
Can I modify or delete audit_logs records?
The generated row triggers block normal UPDATE and DELETE operations, but they are not a privilege boundary: PostgreSQL TRUNCATE does not run row triggers, and a table owner or superuser can alter or bypass them. Use separate runtime and maintenance identities, keep table ownership out of the application role, and revoke UPDATE, DELETE, and TRUNCATE from that runtime role.
What is the difference between automatic tracking and manual logging?
- Automatic tracking: The Prisma extension detects CUD operations and records before/after diffs. In 0.4, choose
atomic-requiredwithwithAuditTransaction()for same-transaction evidence or explicitly select legacybest-effort - Manual logging:
AuditService.log()records business events (e.g., "invoice.approved") explicitly
Both write to the same audit_logs table.
@nestarc/pagination
Should I use cursor or offset pagination?
- Offset: Page numbers, total count display, admin dashboards
- Cursor: Infinite scroll, large datasets, real-time data with consistent results
Cursor mode activates automatically when after/before parameters are present.
What happens when a disallowed filter column is used?
An InvalidFilterColumnError (400) is thrown. Only columns and operators explicitly registered in filterableColumns are allowed. This is a security measure.