This content originally appeared on DEV Community and was authored by BB Dev Team
When you build a platform that handles millions of users and sensitive data, security and reliability are not just features – they’re survival requirements.
In our engineering team, we rely on NestJS (backend) and Next.js (frontend), but we don’t stop there. To ensure maximum security and resilience, we combine battle-tested open-source tools with custom in-house frameworks designed specifically for our threat model and performance needs.
This post gives an overview of how we approach data protection, authentication, validation, system reliability, secure real-time communication, and code security at scale.
Core Security Principles
1) Minimal Data Collection
- Every extra field increases risk.
- For KYC, we persist only the legally required attributes and discard everything else.
2) Encryption Everywhere
- At Rest: All DB fields with sensitive data are encrypted.
- In Transit: TLS 1.3 + HSTS with automatic certificate rotation.
- On Application Layer: Some values (like ID scans) are encrypted before they ever reach the database.
import * as crypto from "crypto";
function encrypt(value: string, key: string) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv("aes-256-gcm", Buffer.from(key, "hex"), iv);
let encrypted = cipher.update(value, "utf8", "hex");
encrypted += cipher.final("hex");
const authTag = cipher.getAuthTag().toString("hex");
return { iv: iv.toString("hex"), data: encrypted, tag: authTag };
}
3) Strong Authentication (2FA)
- Mandatory 2FA for users and admins.
- NestJS Guards make it straightforward to enforce OTP/WebAuthn or second-factor checks on sensitive routes.
import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
@Injectable()
export class TwoFactorGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToHttp().getRequest();
return req.user?.is2FAAuthenticated === true;
}
}
4) Role-Based Access Control
- Implemented via NestJS Guards and decorators.
- Principle: Least Privilege – narrow, task-focused scopes.
5) Auditing & Monitoring
- Centralized logging (e.g., ELK / Datadog) with anomaly detection.
- Alerts on suspicious patterns (login abuse, unusual data exports).
Request & Response Validation with ts-rest + Zod
We use ts-rest combined with Zod to strictly validate both requests and responses. This enforces contracts between backend and frontend and prevents accidental leaks or schema mismatches.
import { initContract } from "@ts-rest/core";
import { z } from "zod";
const c = initContract();
export const authContract = c.router({
login: {
method: "POST",
path: "/auth/login",
responses: {
200: z.object({
accessToken: z.string(),
refreshToken: z.string(),
}),
},
body: z.object({
email: z.string().email(),
password: z.string().min(8),
}),
},
});
Reliability: Microservices & Proxies (API Path)
Security without availability is meaningless. Our API services are designed for fault tolerance:
- Microservices: Isolated domain services reduce blast radius and allow independent scaling.
- Proxies & API Gateway: Centralized entry for routing, rate limiting, schema validation, and WAF.
- Graceful Degradation: If one service fails, proxies reroute/fallback without exposing raw errors.
- Zero-Downtime Deployments with blue-green strategies and rolling updates.
- Circuit Breakers & Retries with idempotency keys to avoid duplicate side effects.
Note: The real-time stack (Socket.IO) is separate from the microservices runtime. See the next section.
Secure Real-Time Communication (Socket.IO Path)
We use a heavily adapted version of Socket.IO for customer chat and live website updates. This channel is independent from the microservices and optimized for reliability and security:
- All socket connections are encrypted end-to-end.
- Custom handshake validation prevents session hijacking.
- Events are schema-validated before leaving or entering the server.
- Proxies apply throttling and IP-based abuse detection.
Why We Use In-House Frameworks
While we like NestJS and Next.js, we intentionally reduce reliance on third-party open-source libraries.
- Many open-source packages are under-maintained and accumulate vulnerabilities.
- In-house frameworks allow us to:
- Control the attack surface.
- Patch vulnerabilities instantly.
- Simplify dependency management.
We selectively integrate OSS only when the maintenance and security posture is strong – and even then, often behind our own abstraction layers.
UI: A Hardened Version of shadcn
For the frontend UI, we rely on a heavily customized version of shadcn/ui.
- It gives us a consistent, accessible component system.
- We stripped unnecessary dependencies and hardened components against XSS and injection risks.
- Our version is tightly integrated with our own design system and security checks (e.g., CSP compliance).
Code Security: Obfuscation, Commit Rules & Tooling
We treat code as a security boundary itself:
- Obfuscators: We run multiple stages of JS/TS obfuscation on build artifacts to hinder reverse engineering.
- Commit Guidelines: Hard rules enforced via Git hooks – no secrets, no TODOs, no debug code in commits.
-
Automated Toolchain: Before deployment, every commit passes through a suite of security tools:
-
eslintwith strict security configs -
ts-pruneto detect unused/forgotten code -
depcheckandnpm auditfor dependency issues -
semgrepfor static code vulnerability scanning -
zap-cli(OWASP ZAP) for lightweight API security scans in CI/CD -
docker-bench-securityfor container hardening checks - Custom secret scanners to detect keys, tokens, or credentials in code
-
This ensures that only secure, hardened, and obfuscated code ever reaches production.
Architecture Overview
[ User ]
│
▼
[ Proxy / WAF ]
│
┌────┴─────────┐
▼ ▼
[ Auth ] [ API Gateway ]
│ │
▼ ▼
[ Microservices ] <-- encrypted APIs
│
▼
[ Encrypted Database ]
(Parallel Stack)
[ User ]
│
▼
[ Secure Socket.IO ] <-- chats & live updates
Key Takeaways
- Collect less, protect more – store the minimum data possible.
- Encrypt at every layer – DB, transit, application.
- 2FA everywhere – users and admins alike.
- Validate requests & responses with ts-rest + Zod.
- Design for failure – microservices + proxies keep the system resilient.
- Harden real-time communication – encrypted and validated Socket.IO.
- Limit external dependencies – in-house frameworks reduce attack surface.
- UI is also security – a hardened design system prevents front-end exploits.
- Secure the code itself – obfuscation, commit rules, and automated security tooling.
Security is never “done.” But by combining strong principles, carefully chosen tools, and a defense-in-depth strategy, you can significantly increase both trust and resilience.
We are the engineering team behind bb-escort.de. On dev.to we share lessons learned about scaling, security, and developer experience.
This content originally appeared on DEV Community and was authored by BB Dev Team