Build vs Buy: When to Use a Library for SaaS Backend Features
Every SaaS backend needs multi-tenancy, audit logging, feature flags, and pagination. The question is: should you build them yourself or use a library?
This isn't a sales pitch. Sometimes building from scratch is the right call. Here's how to decide.
The Real Cost of Building
The initial implementation isn't the expensive part. It's the edge cases you discover in production.
Multi-Tenancy
Looks easy:
// "Just add a WHERE clause"
findAll(tenantId: string) {
return this.prisma.task.findMany({ where: { tenantId } });
}Actually hard:
- What if someone forgets the WHERE clause? Silent data leak.
- What about
$executeRawand$queryRaw? Not filtered. - What about background jobs with no request context?
- What about database migrations that touch multiple tenants?
- What about unique constraints that should be per-tenant?
A production-ready tenant isolation system handles all of these. That's weeks of work, not hours.
Audit Logging
Looks easy:
// "Just log the change"
await this.auditService.log({
action: 'update',
targetType: 'User',
targetId: userId,
metadata: { changedFields: Object.keys(dto) },
});Actually hard:
- What's the "before" state? You need a separate read before each write.
- How do you track batch operations (
updateMany,deleteMany)? - How do you handle sensitive fields (passwords, SSN)?
- What if the audit log insert fails — should the business write fail too?
- How do you ensure 100% coverage? (Hint: manual calls get forgotten.)
Feature Flags
Looks easy:
// "Just check a database table"
const flag = await this.db.featureFlag.findUnique({ where: { key: 'new-ui' } });
if (flag?.enabled) { ... }Actually hard:
- How do you handle percentage rollouts consistently? (Same user must always see the same result.)
- How do you override per-tenant without a cascade of if-statements?
- How do you test routes gated behind flags?
- How do you clean up stale flags without breaking production?
When Building Makes Sense
Build from scratch when:
- Your requirements are unique — your tenancy model doesn't fit the standard patterns (e.g., per-tenant databases, sharding)
- You need deep integration — the library's abstraction doesn't match your ORM, database, or framework
- You're building it as a core competency — you sell a platform where these features are the product
- The scope is truly simple — you have 3 tables, 1 developer, and no compliance requirements
When a Library Makes Sense
Use a library when:
- You need to ship quickly — these are solved problems; re-solving them delays your actual product
- Multiple developers will maintain the code — a library provides consistent patterns across the team
- You need correctness guarantees — tenant isolation and audit logging can't have "oops" moments
- The scope will grow — today it's 3 tables, but SaaS projects rarely stay small
The nestarc Approach
nestarc packages are designed for the "library makes sense" scenario:
| Feature | DIY review focus | Package adoption focus | Ongoing ownership |
|---|---|---|---|
| Multi-tenancy (RLS) | policies, roles, context propagation | extension wiring and policy verification | shared with library, database, and app teams |
| Audit logging | mutation coverage, integrity, retention | base/extended client boundaries | shared; application event coverage remains yours |
| Feature flags | evaluation, rollout, cache invalidation | context and lifecycle configuration | shared; stale-flag cleanup remains yours |
| Soft delete | relation filtering, uniqueness, purge | extension chaining and DB indexes | shared; retention policy remains yours |
| Pagination | query validation, indexes, cursor contract | allowed columns and query plans | shared; schema/index tuning remains yours |
| Response standardization | envelope and compatibility policy | global module and opt-in metadata | shared; client versioning remains yours |
The key advantage is reusable implementation and documented constraints. A package reduces repeated work, but it does not transfer ownership of threat modeling, production validation, upgrades, or incident response.
Questions to Ask
Before deciding, ask yourself:
- "Would I be embarrassed if this had a bug?" — If yes (tenancy, audit), use a tested library.
- "How many developers will touch this code?" — If more than 2, consistency from a library helps.
- "Is this a differentiator for my product?" — If no, don't invest custom engineering time.
- "Will I maintain this in 2 years?" — Compare the package's release history and support status with your ability to own a fork or replacement if maintenance stops.
Further Reading
- Getting Started — set up your first nestarc package in 5 minutes
- Adoption Roadmap — recommended adoption path for first-time users
- Prisma Extension Chaining — how packages compose together
- NestJS Dynamic Modules — the framework mechanism used by configurable Nest modules