Database Patterns

Preview — 3 of 10 questions

You want services to depend on a persistence abstraction, not TypeORM directly. What is the cleanest design in NestJS?

javascript
export interface UserRepositoryPort {
  findByEmail(email: string): Promise<User | null>;
}
export const USER_REPOSITORY = Symbol('USER_REPOSITORY');

@Injectable()
class TypeOrmUserRepository implements UserRepositoryPort {
  constructor(@InjectRepository(User) private repo: Repository<User>) {}
  findByEmail(email: string) { return this.repo.findOneBy({ email }); }
}

// provider: { provide: USER_REPOSITORY, useClass: TypeOrmUserRepository }
// service: constructor(@Inject(USER_REPOSITORY) private users: UserRepositoryPort)
AInject Repository<Entity> everywhere and add if (driver === 'postgres') branches
BDefine a port interface (e.g. UserRepositoryPort) and bind it to a TypeORM adapter via a custom provider token; services depend only on the interface
CWrap every query in a stored procedure
DUse raw SQL strings centralized in one file

Which TypeORM construct best implements a Unit of Work so multiple repository operations commit atomically?

javascript
await dataSource.transaction(async (manager) => {
  const order = await manager.save(orderEntity);
  await manager.insert(LedgerEntry, { orderId: order.id, amount });
  await manager.decrement(Stock, { sku }, 'qty', 1);
  // all commit together, or all roll back
});
AA single dataSource.transaction(async (manager) => ...) where every operation uses that transactional EntityManager
BCalling save() on each repository separately and hoping they all succeed
CSetting synchronize: true
DWrapping calls in Promise.all

For strong data isolation between tenants with shared application code, which strategy gives the strongest isolation, and what is the tradeoff?

javascript
// Schema-per-tenant: resolve a DataSource/QueryRunner that sets search_path
async function withTenant(ds: DataSource, schema: string) {
  const qr = ds.createQueryRunner();
  await qr.connect();
  await qr.query(`SET search_path TO "${schema}"`);
  return qr; // remember to release()
}
ARow-level (a tenantId column) — strongest isolation, lowest operational cost
BSchema-per-tenant (or database-per-tenant) — strongest isolation but higher migration/connection overhead vs. a shared schema with tenantId filtering
COne table per tenant in a shared schema — best for thousands of tenants
DStoring tenants as JSON blobs

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.