Files
nick-tracker/docs/research-notes.md
2026-01-11 07:51:30 +00:00

1240 lines
46 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Research Notes
## Query: NestJS TypeScript REST API WebSocket architecture best practices 2026
For a modern NestJS backend that serves both a **REST API** and **WebSockets**, treat HTTP and WS as firstclass but separate concerns, built on a modular, layered architecture, with clear domain boundaries, shared application services, and infrastructure focused on scaling (Redis, microservices) and robustness.
Below is an architecture and bestpractices checklist you can use as a reference design for 20252026 projects.
---
## 1. Highlevel architecture
**Core principles**
- **Hexagonal / clean architecture**:
- **Domain layer**: entities, value objects, domain services (frameworkagnostic).
- **Application layer**: use cases/services orchestrating domain logic.
- **Interface layer**: REST controllers, WebSocket gateways, GraphQL resolvers, etc.
- **Infrastructure layer**: database adapters, message brokers, cache, 3rdparty APIs.
- **Nest modules for vertical slicing**
- One module per domain (e.g. `UsersModule`, `ChatModule`, `OrdersModule`), each exposing:
- REST **controllers** for HTTP
- **gateways** for WebSocket events
- shared **providers** (services, repositories).
- This modular approach is explicitly recommended for scalable NestJS apps.[7][8]
- **Transport separation, logic reuse**
- REST controllers and WebSocket gateways should **call the same application services**, not duplicate business logic.
- Gateways are just another interface adapter, as described in the Nest docs on WebSocket gateways.[6]
---
## 2. REST API best practices (NestJS + TypeScript)
- **Controllers**
- Thin controllers that:
- Map DTOs to method arguments.
- Delegate to service/usecase classes.
- Map results to HTTP responses.
- Use route versioning (`/v1`, `/v2`) and Nests versioning support for breaking changes.
- **DTOs, validation, transformation**
- Use `class-validator` + `class-transformer` and global `ValidationPipe` for all REST inputs.
- Keep REST DTOs separate from WebSocket payload DTOs when they differ.
- **Error handling**
- Use **global exception filter** (e.g. `HttpExceptionFilter`) to normalize API error shape.
- Map domain errors to HTTP status codes consistently.
- **Security**
- JWT or OAuth2 for auth, Nest Guards for authorization.
- Ratelimit sensitive endpoints with Nest interceptors or a gateway like API Gateway / NGINX.
---
## 3. WebSocket architecture in NestJS
### 3.1. Gateways as interface layer
- **Use Nest WebSocket gateways**
- A gateway class annotated with `@WebSocketGateway()` is the entry point for WS connections.[6]
- Example (Socket.IO):
```ts
@WebSocketGateway({ namespace: '/chat', cors: true })
export class ChatGateway {
@WebSocketServer() server: Server;
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
) {
client.broadcast.emit('message', data);
}
}
```
This pattern is standard in 20242025 NestJS WebSocket guides.[2][3][6]
- **Lifecycle hooks**
- Implement `afterInit`, `handleConnection`, `handleDisconnect` to manage:
- Connection registry (online presence).
- Resource allocation / cleanup.[1][2][3][6]
- **Namespaces and rooms**
- Use **namespaces** per domain (`/chat`, `/notifications`, `/trading`) and **rooms** per context (user, group, document) to keep broadcasting efficient.[2]
### 3.2. WebSocket best practices (20252026)
**Security**[1][2][3][4]
- Authenticate during the **handshake**:
- Pass JWT in query, header, or cookie; validate in a guard or middleware.
- Authorize events:
- Use perevent guards or authorization services.
- Prevent clients from subscribing to rooms they are not allowed to see.
- Validate all payloads:
- Use pipes or schema validation (e.g. Zod) for WS events, similar to HTTP.[4]
**Connection handling**[1][2][3]
- Track connections per user (userId → socketIds).
- Clean up on `handleDisconnect` (presence, locks, subscriptions).
- Avoid longrunning work in WS handlers; delegate to async services/queues.
**Performance & scalability**[1][2][3][7]
- Offload heavy work to:
- Background jobs (Bull/BullMQ), microservices, or message queues.
- Use **Redis pub/sub** or similar for horizontal scaling:
- Shared adapter for Socket.IO so messages propagate across instances.[1][2]
- Monitor:
- Event loop lag, memory, open sockets, message throughput.[2][7]
**Resilience**[1][2]
- Configure clientside **reconnect** and exponential backoff.
- Handle network partitions, stale sockets, and replay/duplicate events at the application layer.
---
## 4. Integrating REST and WebSockets cleanly
- **Pattern: REST for CRUD, WS for realtime**
- REST:
- Resource creation, updates, queries, pagination.
- WebSocket:
- Push updates (notifications, status changes, live data).
- Collaborative operations (presence, typing indicators, etc.).
- **Event flow (typical pattern)**
1. Client calls REST API to change state (e.g. create message).
2. Application service persists change, publishes domain event.
3. WS gateway (or a microservice) listens to events and **broadcasts** to interested clients (rooms/users).
- **Avoid duplicating write paths**
- Prefer **one canonical mutation path** (often REST or a command bus) and use WS mostly for reads/updates propagation.
- If you accept writes over WS (e.g. chat messages), a service should handle both HTTP and WS commands identically.
---
## 5. Scaling and microservices
- **Nest microservices**
- Nest supports microservices and multiple transports (Redis, NATS, Kafka, MQTT, etc.).[5]
- Use microservices for:
- Hightraffic domains (chat, notifications, analytics).
- Decoupling heavy realtime loads from main REST API.
- **Pattern**
- API gateway / BFF exposes REST + WebSocket.
- Internal microservices handle business logic and communicate via message broker.
- Gateway uses Redis pub/sub or broker to broadcast results to WS clients.
---
## 6. Crosscutting concerns (2026ready)
- **Type safety**
- Use TypeScript generics and shared **contract types** for REST DTOs and WS payloads.
- Consider a shared package (`@app/contracts`) for request/response/event types.
- **Logging and observability**[1][2][3][7]
- Structured logging from controllers and gateways (requestId / correlationId).
- Metrics: number of connections, messages per second, failed events, response times.
- Tracing: OpenTelemetry across REST and WS flows.
- **Configuration & environment**
- Use Nest `ConfigModule` for environmentbased configuration (DB/Redis URLs, CORS, allowed origins).
- Keep WS CORS rules aligned with HTTP CORS.
- **Testing**
- Unit test:
- Services and domain logic in isolation.
- Integration test:
- REST endpoints via `@nestjs/testing`.
- WebSockets using Socket.IO client in tests to assert message flows.
---
## 7. Practical checklist
When designing a NestJS REST + WebSocket service for 2026, ensure:
- **Architecture**
- Domaindriven modules, clean layering, shared services.
- **REST**
- Versioned, validated, documented (Swagger), with unified error format.
- **WebSockets**
- Gateway per domain, namespaces/rooms, lifecycle hooks, validated payloads, authenticated handshakes.[1][2][3][6]
- **Scaling**
- Redis adapter for WS, microservices or queues for heavy tasks.[1][2][5][7]
- **Security & robustness**
- Guards, ratelimit on HTTP, message validation, connection management, observability.[1][2][3][4][7][8]
If you share your specific use case (e.g., trading app, collaborative editor, chat, IoT), I can propose a concrete module layout and message/REST design aligned with these practices.
### Sources
- [Source 1](https://saigon.digital/blog/implementing-websockets-with-nestj/)
- [Source 2](https://www.videosdk.live/developer-hub/websocket/nest-js-websocket)
- [Source 3](https://dev.to/ezilemdodana/real-time-applications-with-nestjs-and-websockets-5afk)
- [Source 4](https://blog.stackademic.com/nestjs-websockets-authentication-authorization-pipes-f18605e3c393)
- [Source 5](https://docs.nestjs.com/microservices/basics)
- [Source 6](https://docs.nestjs.com/websockets/gateways)
- [Source 7](https://www.brilworks.com/blog/optimize-your-nest-js-app-performance/)
- [Source 8](https://fabwebstudio.com/blog/building-scalable-and-secure-enterprise-applications-with-the-nest-js-framework)
## Query: React Vite calendar drag-drop FullCalendar react-big-calendar performance optimization 2026
For heavy drag-and-drop calendar UIs in React/Vite (e.g., FullCalendar or react-big-calendar) with many events, you get the best performance by combining **library-level optimizations** (virtualization, progressive/partial rendering) with **React-level memoization and minimal rerenders**.
Below is a focused checklist, split by library and then general React/Vite strategies.
---
## 1. When to use FullCalendar vs react-big-calendar vs alternatives
- **FullCalendar**
- Strong feature set (drag/drop, resource views, Scheduler, etc.).
- Historically suffers when rendering many events because it **renders all events in the DOM** and historically re-renders too much on drag/drop.[2]
- From **v6.1.18**, event rerendering was optimized so that *only modified events* are rerendered, not all.[2]
- Roadmap: v7 adds optimizations; v7.1+ mentions **virtual rendering** as a goal.[2] v8/v9 roadmap continues performance work.[4]
- **react-big-calendar**
- React-friendly; uses Flexbox instead of table layout, which was originally cited as a possible performance improvement over FullCalendars table layout.[2]
- No builtin virtualization; performance drops with hundreds+ of events similar to FullCalendar.[5]
- **High-performance alternatives for 20252026**
- **Bryntum Calendar / Scheduler**: virtual rendering, minimal DOM footprint, advanced performance features.[3][5]
- **DayPilot**: progressive rendering, on-demand loading, partial updates, optimized for heavy workloads.[3]
- **Planby**: React timeline/calendar component with **virtual rendering**, reported ~3× faster than FullCalendar for 500+ events.[1][3]
If you need **thousands of events with smooth drag/drop**, consider Bryntum, DayPilot, or Planby before trying to push FullCalendar/react-big-calendar to their limits.[1][3][5]
---
## 2. FullCalendar + React/Vite performance strategies
### 2.1 Use the latest FullCalendar with optimized rerenders
- Use **FullCalendar v6.1.18+** or v7 when available:
- Event updates now rerender **only modified events**, fixing the “all events rerender on drag/drop or update” issue.[2]
- This greatly cuts CPU time when dragging or updating single events in large views.
- For frequent serial updates (e.g., rapidly mutating events), use:
```js
const options = {
rerenderDelay: 100, // ms
};
```
This batches rerenders and significantly reduces main-thread work.[2]
### 2.2 Reduce DOM and event complexity
- Filter data **before** passing it to FullCalendar.
- Only pass events in (or near) the visible date range instead of your whole dataset.
- Use backend pagination or API parameters to fetch only what is needed.
- Avoid unnecessary custom DOM in `eventContent`:
- Keep event render content minimal; heavy React trees inside each event will dominate render cost.
- Prefer simple markup and minimal React state inside event content.
### 2.3 Avoid re-creating props on every render
In your React wrapper around FullCalendar:
- Memoize **events** and other large props:
```ts
const events = useMemo(() => transformRawEvents(rawEvents), [rawEvents]);
```
- Memoize callbacks passed to FullCalendar (e.g., `eventDrop`, `eventClick`) using `useCallback` so React doesnt think props changed every render.
### 2.4 Defer heavy work off the main thread
- For large transforms (e.g., normalizing thousands of events), use:
- Web Workers
- Debounced/batched updates (e.g., only recompute after user stops dragging for X ms).
- Precompute layout data on the server if possible (e.g., start/end times and conflicts) so the client only renders.
---
## 3. react-big-calendar performance strategies
react-big-calendar lacks built-in virtualization, so the focus is on **minimizing React work**:
- Use **`React.memo`** for all custom components (event renderer, toolbar, custom headers).
- Memoize the `events` array and **do not recreate it** on every render:
```ts
const events = useMemo(() => toBigCalendarEvents(rawEvents), [rawEvents]);
```
- Avoid storing large event lists in multiple layers of state; one source of truth is enough.
- When drag/drop is enabled:
- Update the single affected event in place and reuse the same array reference when possible, or use a keyed immutable update that doesnt require rebuilding the whole list.
- Keep custom event and slot renderers simple; avoid heavy trees inside each cell.
If you still experience lag with 1000+ events, consider switching to a **virtualized scheduler** (Bryntum/DayPilot/Planby).[1][3][5]
---
## 4. General React performance patterns (for any calendar)
These apply for FullCalendar, react-big-calendar, or alternatives:
- **Avoid global rerenders**
- Use state libraries that support fine-grained updates (e.g., Zustand, Jotai, Redux with careful selectors) so updating one event doesnt rerender the entire app.
- **Memoize** everything passed into the calendar:
- `events`, `resources`, `views`, handlers like `onEventDrop`, `onSelectSlot`, etc.
- **Virtualization where possible**
- If your calendar library exposes a way to control rendering of rows or resources, implement your own virtualization or use a library that already does this (Bryntum, DayPilot, Planby).[1][3][5]
- **Throttle drag/drop-driven updates**
- Do not persist to server or update global state on every drag movement.
- Use `onEventDrop` (or equivalent) for final commits; only show local/optimistic feedback during drag.
- **Keep React DevTools and logs off** in production; they can distort performance tests.
---
## 5. Vite-specific considerations
Vite itself is very fast; issues are nearly always in runtime React, not bundling. Still:
- Use production builds (`vite build` + serve) when testing performance; dev mode adds overhead.
- Configure code splitting to keep the calendar and its heavy dependencies in separate chunks so initial load is smaller.
- Avoid bundling multiple calendar libraries simultaneously unless needed.
---
## 6. If youre starting a new project in 20252026
For a **React + Vite calendar with drag/drop and many events**:
- If you need **enterprise-level scale** (thousands of events, many resources, smooth interaction):
- Consider **Bryntum Calendar/Scheduler** or **DayPilot React Scheduler** for built-in virtual rendering and progressive loading.[3][5]
- If you need a **timeline-style UI** (TV guide, bookings, shifts) with virtual scroll:
- Consider **Planby**, which was specifically built for large event sets and is reported ~3× faster than FullCalendar with 500+ events.[1]
- If you are already invested in **FullCalendar**:
- Upgrade to the latest v6.1.18+ and adopt `rerenderDelay`, data filtering, and memoization as above.[2][3]
If you share your current stack (FullCalendar or react-big-calendar), approximate event count, and whether you use resources/timeline views, I can propose a more concrete configuration and small code snippets tailored to your case.
### Sources
- [Source 1](https://dev.to/kozerkarol/how-i-built-a-lightweight-react-calendar-thats-3-faster-than-fullcalendar-1bj)
- [Source 2](https://github.com/fullcalendar/fullcalendar/issues/3003)
- [Source 3](https://dhtmlx.com/blog/best-react-scheduler-components-dhtmlx-bryntum-syncfusion-daypilot-fullcalendar/)
- [Source 4](https://fullcalendar.io/roadmap)
- [Source 5](https://bryntum.com/blog/react-fullcalendar-vs-big-calendar/)
- [Source 6](https://sourceforge.net/software/product/FullCalendar/)
- [Source 7](https://www.youtube.com/watch?v=keTcXT145CI)
- [Source 8](https://javascript.plainenglish.io/reacts-top-libraries-for-2026-the-ultimate-guide-to-building-faster-cc1415d7fc4c)
## Query: NestJS PostgreSQL Docker Compose self-hosted deployment security best practices
For a selfhosted NestJS + PostgreSQL stack on Docker Compose, focus on hardening **containers**, **networking**, **secrets**, and **database** configuration, plus general app security.
Below is a concise checklist you can adapt.
---
## 1. Docker & Compose hardening
- **Use multistage builds & small base images**
- Build in a `builder` stage and copy only the compiled `dist` + production deps into a minimal runtime (e.g. `node:alpine`).[3][5]
- Avoid shipping dev tools, build tools, tests, and `.git` into the final image.
- **Run as nonroot**
- Set `USER node` (or another unprivileged user) in the final stage, not root.[3][4]
- Ensure mounted volumes and files are readable by that user, not worldwritable.
- **Set production environment**
- `NODE_ENV=production` in the final image so that frameworks and libraries use hardened, production defaults.[3]
- **Readonly filesystem where possible**
- For the API container, keep the filesystem mostly readonly and write only to explicit volumes (logs, temp, etc.).
- **Limit container capabilities**
- In `docker-compose.yml` add:
```yaml
cap_drop:
- ALL
read_only: true
```
and selectively add what you truly need.
---
## 2. Network isolation & exposure
- **Use a private Docker network**
- Attach NestJS and PostgreSQL to a **dedicated userdefined network** so only those services can talk to each other.[2]
- Do **not** publish the DB port on the host unless truly required:
```yaml
services:
api:
networks:
- app_net
db:
networks:
- app_net
# avoid: ports: ["5432:5432"]
networks:
app_net:
driver: bridge
```
- **Restrict PostgreSQL listen addresses**
- In `postgresql.conf`, set:
```conf
listen_addresses = '0.0.0.0' # inside Docker; but reachable only via app_net
```
or even the explicit container IP if you manage it carefully.[2]
- **Single public entrypoint**
- Only expose the NestJS container (or, better, a reverse proxy like Nginx/Traefik) to the internet.
- PostgreSQL must never be directly reachable from the public network.
---
## 3. Secrets & configuration (NestJS + Postgres)
- **Avoid hardcoded secrets and plain env files**
- Do *not* check `.env` into version control.
- For production, use **Docker secrets** or an external secret manager:
```yaml
services:
db:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/pg_passwd
secrets:
- pg_passwd
secrets:
pg_passwd:
external: true
```[2]
- **Separate config per environment**
- Use different `.env`/secret sets for dev, staging, prod.
- Ensure DB name/user/password differ per environment.
- **NestJS config management**
- Use `@nestjs/config` or a similar centralized config module; never commit secrets into code.
- Validate config (e.g. via `Joi`) on startup to avoid misconfigurations.
---
## 4. PostgreSQL security in Docker
- **Use a maintained image & pinned versions**
- Use official/maintained images (`postgres:X.Y` or `bitnami/postgresql`) and pin a major/minor version to avoid surprises.[2][3]
- **Strong credentials and least privilege**
- Use strong passwords for the `POSTGRES_USER` and for the application DB user.
- Create a dedicated DB user for the NestJS app with only required privileges (no `SUPERUSER`).
- **Persistent volumes with proper permissions**
- Mount a volume for data:
```yaml
volumes:
- pgdata:/var/lib/postgresql/data
```
- Ensure the volume is only accessible to the postgres user inside the container.
- **TLS/SSL for DB connections**
- For selfhosting where network hops extend beyond a single host (or you dont fully trust the network), enable **SSL/TLS** in PostgreSQL and require SSL in the NestJS connection string.[2]
- **Connection limits & resource tuning**
- Set reasonable limits (e.g. `max_connections`, `work_mem`) to mitigate DoS from misbehaving app instances.
---
## 5. NestJS application security
- **Frameworklevel security**
- Disable detailed error messages and stack traces in production.
- Use security middleware: Helmet for HTTP headers, ratelimiting, and CORS with strict allowed origins.[1]
- **Authentication & session security**
- Use a vetted auth library (JWT, session management, or a solution like SuperTokens) and store tokens securely (e.g. HTTPonly cookies).[1]
- Implement CSRF protection if you use cookiebased auth.
- **Input validation & sanitization**
- Use `class-validator` and `class-transformer` globally to validate DTOs.
- Sanitize inputs to mitigate injection; never concatenate untrusted data into SQL—use an ORM or parameterized queries.
- **Logging & monitoring**
- Centralize logs per container and avoid logging secrets.
- Enable basic health checks in Docker (e.g. `pg_isready` for Postgres, `/health` endpoint for NestJS).[1][3]
---
## 6. Compose example (securityaware skeleton)
```yaml
version: "3.9"
services:
api:
build:
context: .
dockerfile: Dockerfile
environment:
NODE_ENV: production
DB_HOST: db
DB_PORT: 5432
DB_USER: app_user
DB_NAME: app_db
# DB_PASSWORD via secret or env in production
depends_on:
- db
networks:
- app_net
ports:
- "80:3000"
restart: unless-stopped
user: "1000:1000" # non-root
read_only: true
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app_user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- app_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app_user"]
interval: 5s
timeout: 5s
retries: 5
secrets:
- db_password
networks:
app_net:
driver: bridge
volumes:
pgdata:
secrets:
db_password:
external: true
```
---
## 7. Operational practices
- Keep images up to date with security patches; automate rebuilds and deploys.
- Regularly back up PostgreSQL (physical or logical backups) and test restores.
- Limit SSH and Docker daemon access; treat it as production infrastructure.
- Use a firewall / security groups to only allow app and admin traffic where needed.
If you share your current `Dockerfile` and `docker-compose.yml`, I can point out specific changes to harden your setup.
### Sources
- [Source 1](https://supertokens.com/blog/how-to-secure-a-nestjs-app)
- [Source 2](https://sliplane.io/blog/best-practices-for-postgres-in-docker)
- [Source 3](https://dev.to/manuchehr/dockerize-secure-nestjs-app-with-postgres-redis-56md)
- [Source 4](https://wanago.io/2023/01/16/api-nestjs-docker-compose/)
- [Source 5](https://forums.docker.com/t/best-practices-for-using-docker-in-development-vs-production-nestjs-nextjs-monorepo/149461)
- [Source 6](https://blog.logrocket.com/containerized-development-nestjs-docker/)
- [Source 7](https://hackernoon.com/how-to-set-up-postgresql-with-nestjs-and-docker-for-fast-local-development-a-quick-guide)
## Query: TypeScript monorepo project structure NestJS React common mistakes pitfalls
A solid TypeScript monorepo with **NestJS (backend)** and **React (frontend)** usually fails not because of tools, but because of structure and boundaries. Below are the most common mistakes and how to avoid them, specifically for a Nest + React monorepo.
---
## 1. Treating the monorepo as one big TS project
**Mistake:** Single `tsconfig.json` at the root, all code in one program, no **project references**.
**Problems:**
- Slow `tsc` and IDE responsiveness as the repo grows.[3][5]
- No clear build order between backend, frontend, and shared libs.
- Harder to run isolated builds in CI.
**Better:**
- Root `tsconfig.base.json` with shared compiler options.[1][3]
- Each app/lib has its own `tsconfig.json` and uses **`references`** to shared packages.[3][5]
- Build with `tsc --build` (or via Nx/Turbo) from root so TS respects the dependency graph.[3][5]
---
## 2. No clear separation between apps and shared libraries
**Mistake:** Nest and React importing each others code directly via relative paths instead of through **shared packages**.
**Problems:**
- Circular dependencies (e.g., React importing from `apps/api/src/...` and Nest importing from `apps/web/src/...`).
- Accidental leaking of backend-only code to the frontend bundle (e.g., Node APIs in browser).
**Better structure:**
- `apps/api` NestJS app
- `apps/web` React app
- `packages/shared-domain` pure domain logic, types, DTOs (no Nest/React-specific code)
- `packages/shared-config` environment/config types, config helpers (no framework globals)
Use **package boundaries**:
- Frontend imports only from `packages/*`.
- Backend imports from `packages/*` plus its own `apps/api/*`.
---
## 3. Sharing “too much” code between Nest and React
**Mistake:** Putting everything common (including Nest decorators, pipes, React hooks) into one “shared” package.
**Problems:**
- Shared package becomes framework-dependent and unusable on the other side.
- React app may accidentally import Nest-only code, causing bundling/runtime failures.
**Better:**
- Keep **shared packages framework-agnostic**: domain models, validation schemas, DTOs, API types.
- Have framework-specific adapters:
- `packages/nest-adapters` (uses shared DTOs but also Nest decorators).
- `packages/react-hooks` (uses shared types/DTOs but React-specific logic).
---
## 4. Ignoring module boundaries and coupling Nest modules with React routes
**Mistake:** Nest modules knowing about React routing or component structure, or React directly calling Nest internal modules instead of HTTP APIs.
**Problems:**
- Tight coupling across layers; refactoring either side becomes expensive.
- Impossible to test backend separately without frontend.
**Better:**
- The **boundary between Nest and React is always a protocol**:
- REST/GraphQL schema, or
- shared **API type definitions** in a common package.
- React talks only to HTTP endpoints; Nest exposes controllers/services internally, not React-specific abstractions.
---
## 5. Bad path alias and import strategy
**Mistake:**
- Using long relative paths (`../../../`) everywhere.
- Path aliases defined differently in **TypeScript vs bundler** (e.g., Vite/Webpack) vs Node runtime.
- Using `tsconfig` paths without aligning them with your workspace tool.[1][3][7]
**Problems:**
- Code compiles in editor but fails at runtime.
- Confusing circular imports and build errors.
**Better:**
- Define **root-level** `tsconfig.base.json` with `baseUrl` and `paths` and extend it from app/lib `tsconfig`s.[1][3][7]
- Make sure bundler and test runner resolve aliases the same way (e.g., Jest `moduleNameMapper`, Vite/Webpack `alias`).
- Use package imports (`@project/shared-domain`) instead of deep internal paths where possible.
---
## 6. Missing or misusing workspace tooling (Yarn/NPM/pnpm + Nx/Turbo)
**Mistake:**
- Manual `cd apps/api && npm run build` everywhere.
- No topological build order or caching.[3][4]
**Problems:**
- Rebuilding everything on every CI run.
- Subtle build-order bugs: React built before the shared package it uses, etc.
**Better:**
- Use **workspaces** (Yarn/pnpm/npm) for package linking and dependency management.[2][3][4]
- Use a monorepo tool like **Nx** or **Turborepo** to:
- infer dependency graph,
- run `build`/`test` in **topological order** with caching.[3][4]
- Expose a single root command: e.g. `nx run-many --target=build` or `yarn workspaces foreach --topological-dev run build`.[3][4]
---
## 7. Inconsistent tooling config (ESLint, Prettier, Jest/Vitest)
**Mistake:**
- Each app has its own slightly-different ESLint/Prettier/Jest config.
- Some packages use strict TS rules, others dont.
**Problems:**
- Inconsistent code quality and formatting.
- Harder onboarding and surprise build failures.
**Better:**
- Root **shared config** files:
- `eslint.base.js` and app-level small extensions.
- `prettier.config` at root.[5]
- Shared Jest/Vitest base config; each app adds its own transforms.
- Ensure test runners understand TS project references and path aliases.
---
## 8. Wrong granularity of packages
**Mistake:**
- Either: one giant `shared` package with everything.
- Or: dozens of tiny packages for every small utility function.
**Problems:**
- Giant shared package: no clear boundaries, difficult to version.
- Tiny packages: dependency graph and tooling overhead become unmanageable.
**Better:**
- Package around **cohesive domains**, not individual functions:
- `shared-domain`, `shared-api-types`, `shared-ui` (if you truly have cross-app UI), etc.
- Keep packages **independent and acyclic**: avoid cycles in dependencies.[5]
---
## 9. Not using TypeScript project references correctly
**Mistake:**
- Setting `references` in `tsconfig` but still running plain `tsc` or `tsc -p` without `--build`.[3]
**Problems:**
- You get none of the incremental build benefits.
- Editors and CI may behave differently.[3][5]
**Better:**
- Use `tsc --build` (or `tsc -b`) from the root to respect project references and incremental builds.[3][5]
- Ensure each referenced project has:
- `"composite": true`
- `"declaration": true`
- Use watch mode (`tsc -b --watch`) during development where appropriate.[5]
---
## 10. Environment and config confusion between Nest and React
**Mistake:**
- Using the same `.env` or config loading code in both server and client without differentiating secrets vs public values.
- Directly importing server-only config from React.
**Problems:**
- Secrets leaked to frontend bundles.
- Hard-to-debug environment mismatch between apps.
**Better:**
- Shared **config types** in a package (`Config`, `PublicConfig`).
- Implementation separated:
- Nest reads from process env, files, secrets managers.
- React uses build-time env injection (e.g., Vite/CRA/Next pattern).
- Only **public subset** goes to the client.
---
## 11. Sharing runtime-only Nest/Node constructs with React
**Mistake:**
- Putting Node-only things (e.g., `fs`, Nest interceptors) in “shared” code imported by React.
**Problems:**
- Bundle fails at build or runtime in browser.
- Tree-shaking doesnt always save you.
**Better:**
- Shared packages should be **runtime-agnostic** or clearly server-only/client-only.
- Use separate entry points:
- `main.ts` / `server.ts` in Nest.
- `index.tsx` / `client.tsx` in React.
- If you must share code that is server-only, keep it in a `packages/server-*` namespace, never used by React.
---
## 12. CI/CD not aligned with monorepo structure
**Mistake:**
- CI builds and tests every app and package on every change.
- Or worse, CI doesn't understand cross-package dependencies.
**Problems:**
- Slow pipelines, flaky deployments.
- Backend deployed without updated shared package, etc.
**Better:**
- Use your monorepo tools **affected**/changed commands:
- Nx “affected” commands, or Turborepos hashing/caching.
- Ensure CI:
- Restores cache for `node_modules` and build outputs.
- Runs builds in dependency order.
- Optionally: one Docker image at root containing all services instead of one per service if that fits your deployment model.[4]
---
If you share your current structure (folders + `tsconfig`s) I can point out concrete, repo-specific pitfalls and give an adjusted layout for Nest + React + shared packages.
### Sources
- [Source 1](https://graphite.com/guides/typescript-in-monorepo)
- [Source 2](https://news.ycombinator.com/item?id=32594915)
- [Source 3](https://nx.dev/blog/managing-ts-packages-in-monorepos)
- [Source 4](https://escape.tech/blog/setup-typescript-monorepo/)
- [Source 5](https://dev.to/mxro/the-ultimate-guide-to-typescript-monorepos-5ap7)
- [Source 6](https://blog.bitsrc.io/monorepo-from-hate-to-love-97a866811ccc)
- [Source 7](https://monorepo.tools/typescript)
- [Source 8](https://www.youtube.com/watch?v=TeOSuGRHq7k)
## Query: NestJS scheduled jobs CalDAV Microsoft Graph API integration testing approaches
Youre essentially looking for **ways to test a NestJS app that has scheduled jobs which talk to CalDAV and Microsoft Graph** (calendar sync-type scenario). Below is a concise strategy focusing on *testing approaches*, not full implementation.
---
## 1. Architectural testability preconditions
To make testing possible, structure your code so that:
- A **scheduler layer** only triggers methods on a **domain/service layer**.
- The domain/service layer depends on interfaces like:
- `CalDavClient` (e.g. `ICalendarProvider` / `ICalDavClient`)
- `GraphClient` (Microsoft Graph)
- Actual HTTP calls are only in those client classes; they are **injected** via Nest DI (`@Injectable` providers with `useClass`/`useFactory`).[2]
This lets you:
- Unit test the service by **mocking clients**.
- Integration test by **swapping real vs fake HTTP** implementations.
---
## 2. Testing scheduled jobs (NestJS Cron / Scheduler)
Assuming you use `@nestjs/schedule`:
- Put schedule decorators on a thin job class:
```ts
@Injectable()
export class CalendarSyncJob {
constructor(private readonly syncService: CalendarSyncService) {}
@Cron('0 * * * *') // every hour, for example
async handleCron() {
await this.syncService.syncAllAccounts();
}
}
```
### Unit test of the job:
- Use `@nestjs/testing` and **mock `CalendarSyncService`**:
```ts
const module = await Test.createTestingModule({
providers: [
CalendarSyncJob,
{
provide: CalendarSyncService,
useValue: { syncAllAccounts: jest.fn().mockResolvedValue(undefined) },
},
],
}).compile();
const job = module.get(CalendarSyncJob);
await job.handleCron();
expect(syncService.syncAllAccounts).toHaveBeenCalledTimes(1);
```
No real time passes, no network involved: you call `handleCron()` directly.
---
## 3. Unit testing CalDAV / Graph integration services
Example service:
```ts
@Injectable()
export class CalendarSyncService {
constructor(
private readonly caldavClient: CalDavClient,
private readonly graphClient: GraphClient,
) {}
async syncUser(userId: string) {
const caldavEvents = await this.caldavClient.getEvents(userId);
const msEvents = await this.graphClient.getEvents(userId);
// diff + write changes
}
}
```
### Unit test approach:
- Replace `CalDavClient` and `GraphClient` with **Jest mocks** or fake implementations.
- Cover:
- Happy path (events synced correctly).
- Conflicts / duplicates.
- Error handling (CalDAV fails, Graph fails, partial sync).
- Assert:
- Correct calls to `.createEvent`, `.updateEvent`, `.deleteEvent`, etc.
- Correct transformation between CalDAV and Graph schemas.
No Nest specifics required beyond DI; this is plain unit testing.
---
## 4. Integration testing with Nests testing module
Here you want to test the **real Nest module wiring** but still avoid hitting external systems.
### Strategy
1. Use `Test.createTestingModule` with your real modules.
2. Override external clients with **HTTP-mocking or fake in-memory servers**.
Examples:
- For HTTP clients (Axios, `@nestjs/axios`):
- Use `nock` or similar to mock CalDAV/Graph endpoints.
- Alternatively:
- Provide fake `CalDavClient`/`GraphClient` that behave like a small in-memory server.
```ts
const module = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(CalDavClient)
.useClass(FakeCalDavClient)
.overrideProvider(GraphClient)
.useClass(FakeGraphClient)
.compile();
```
Then:
- Resolve your `CalendarSyncService` or job and call it.
- Assert on side effects (DB state, logs, events).
This validates Nest DI wiring and internal logic while avoiding real network calls.
---
## 5. Endtoend tests (E2E) with “real” external systems
If you need highconfidence tests:
- Spin up:
- **Test CalDAV server** (e.g. Radicale or DAViCal in Docker).
- **Microsoft Graph test tenant** (with dedicated test accounts).
- Use Nest E2E tests (`@nestjs/testing` + `supertest`) to:
- Call REST endpoints that trigger sync, or
- Call job handlers directly while Nest app is bootstrapped as in production.
Key ideas:
- Use **separate env/config** for E2E: test credentials, test URLs.
- Clean up test data (delete created events) at the end of each test suite.
These tests are slower and brittle; run them in CI only, not on every quick dev run.
---
## 6. Dealing with time / schedule semantics
Scheduled jobs are timebased by nature; tests should not depend on real time:
- **Never** wait for cron triggers in tests.
- Expose job handlers (e.g. `handleCron()`) and call directly.
- If your code uses `Date.now()` / `new Date()` for “now”:
- Inject a `Clock` or use Jests fake timers to control time.
- This is important for tests like “events starting in next 10 minutes”.
---
## 7. Authentication / tokens for Graph & CalDAV
For unit/integration tests with mocks:
- Dont generate real tokens—mock the token provider / auth client.
- If you must hit real Graph:
- Use **apponly auth** with client credentials for tests (service principal).
- Store secrets in CIs secret store; load via Nest config module.[2]
For CalDAV:
- Use dedicated test users with credentials stored in env variables for E2E; mock them in unit/integration tests.
---
## 8. Testing concurrency and request context (optional)
If your scheduled jobs need *pertenant context* or something similar, you might use **AsyncLocalStorage** or `nestjs-cls` to carry contextual data through async calls.[1]
For tests:
- When calling job handlers directly, explicitly set the context:
- Either via your own context services `runWithContext(...)`.
- Or by injecting `ClsService` and manually `set` needed values before calling the service.[1]
This ensures the same context behavior as in HTTP requests but within scheduled jobs.
---
## 9. Recommended layering for testability
A practical layering that makes all of the above straightforward:
- **Job layer**
- `CalendarSyncJob` (only schedule + call into service).
- **Domain/service layer**
- `CalendarSyncService` (contains sync logic).
- Depends only on interfaces + repository (DB) + clock.
- **Integration layer**
- `CalDavClient` (wraps CalDAV HTTP).
- `GraphClient` (wraps Microsoft Graph SDK/HTTP).
- **Infrastructure**
- Config, auth/token provider, HTTP module, repositories.
Then:
- Unit tests target domain/service.
- Integration tests target domain + fake clients/DB.
- E2E tests target whole app + real external systems.
---
If you share your current Nest module layout (which modules/services you have), I can outline very concrete test module setups and example Jest test files for each level (unit, integration, E2E) tailored to CalDAV + Graph.
### Sources
- [Source 1](https://docs.nestjs.com/recipes/async-local-storage)
- [Source 2](https://www.freecodecamp.org/news/the-nestjs-handbook-learn-to-use-nest-with-code-examples/)
- [Source 3](https://www.youtube.com/watch?v=8_X0nSrzrCw)
- [Source 4](https://www.youtube.com/watch?v=2byT7fYT8UE)
- [Source 5](https://www.geeksforgeeks.org/node-js/how-to-integrate-google-calendar-in-node-js/)
- [Source 6](https://developers.google.com/workspace/calendar/api/quickstart/nodejs)
- [Source 7](https://docs.nestjs.com/techniques/events)
## Query: PostgreSQL real-time updates WebSocket NestJS concurrency performance optimization 2026
You can build high-performance realtime updates on PostgreSQL + NestJS + WebSockets, but you must be deliberate about where you get change events from and how you fan them out to clients.
Below is a concise, productionoriented approach for 2025/2026era stacks.
---
## 1. Choose your Postgres → “events” mechanism
**Avoid polling** for anything beyond trivial loads. Use one of:
### A. `LISTEN/NOTIFY` + WebSocket bridge (simple, good for moderate scale)
- Postgres emits events via `pg_notify` from triggers.
- A small service listens via `LISTEN` and pushes over WebSockets to clients.[3][6]
- Example of a bridge: **pg_eventserv**, which converts LISTEN/NOTIFY into standard WebSocket messages to web clients.[3]
**Pros:**
- Simple mental model; small infra footprint.[3]
- Great latency (single roundtrip DB → app → WS).
**Cons & constraints:**
- `NOTIFY` payloads are limited (~8KB).
- `LISTEN/NOTIFY` is not designed for very high fanout or tens of thousands of topics.
- No builtin message durability; if consumers disconnect, they miss events.[3]
**When to use:**
Dashboards, admin panels, low/mediumtraffic SaaS, 100slow 1000s of concurrent WebSocket clients.
---
### B. Logical replication / WAL streaming (scales much better)
Use **logical replication slots** (or a library built on them) to stream changes, then fan them out.
- Trigger.dev describes using **Postgres replication slots + ElectricSQL** as their realtime backbone.[4]
- Flow: Postgres writes to WAL → replication slot captures changes → ElectricSQL processes and pushes to clients via longpoll/WS.[4]
**Performance numbers from Trigger.dev:**
- ~**20,000 updates/second** processed.
- **500GB+** of Postgres inserts daily.
- **Sub100ms latency** to browsers.[4]
**Pros:**
- Much higher throughput and lower DB overhead than triggers + NOTIFY.[4]
- Can support **historical subscriptions** (subscribe to objects created before opening the page).[4]
- Strong consistency guarantees; Postgres remains the single source of truth.[4]
**Cons:**
- More infra and operational complexity (replication slots, separate service like ElectricSQL or your own changeconsumer)[4].
- Need to ensure replication slots dont bloat WAL.
**When to use:**
Highthroughput realtime feeds, large multitenant apps, “activity feed” or “runs/jobs” style products at scale.
---
### C. External realtime services on top of Postgres
If you do not want to manage the event bridge:
- **Supabase Realtime**
- Elixir/Phoenix service that can **listen to Postgres changes and send them over WebSockets**, plus broadcast and presence features.[2]
- Works via logical replication or CDC extensions (`postgres_cdc_rls`).[2]
- **Ably LiveSync + Postgres**
- Neons guide shows using serverless Postgres with an **outbox table + trigger that calls `pg_notify`**, and Ably for WS fanout.[5]
**When to use:**
You want realtime updates, presence, and fanout without writing the whole infra yourself.
---
## 2. NestJS architecture for WebSockets + concurrency
### A. NestJS WebSocket gateway
Use `@WebSocketGateway()` and channels/rooms per logical subscription:
```ts
@WebSocketGateway({ cors: { origin: '*' } })
export class RealtimeGateway {
@WebSocketServer()
server: Server; // for socket.io
@SubscribeMessage('subscribeToItem')
handleSubscribe(
@MessageBody() data: { itemId: string },
@ConnectedSocket() client: Socket,
) {
client.join(`item:${data.itemId}`);
}
publishUpdate(itemId: string, payload: any) {
this.server.to(`item:${itemId}`).emit('item:update', payload);
}
}
```
Your Postgresevent consumer (LISTEN/NOTIFY or WAL) injects the gateway and calls `publishUpdate`.
### B. Concurrency and scaling
To scale beyond a single NestJS instance:
- Run NestJS behind a load balancer.
- Use **sticky sessions** for WS if you use inmemory rooms, or move to **Redis adapter** for socket.io so rooms work across nodes.
- Offload expensive work (e.g. heavy projections) to background workers; gateway process should be light and mostly I/O.
For **write contention** in Postgres:
- Keep transactions short and indexes lean.
- Where possible, use **appendonly tables** (events, logs, runs) instead of frequent UPDATEs; this plays well with WALbased approaches.[4]
---
## 3. Reducing load on Postgres
Across the realtime designs above, common optimizations:
- **Initial state vs. live updates**
- Client fetches initial state via a **regular HTTP/REST/GraphQL** request (can be cached).
- WebSocket is only for **incremental updates**, not full requeries.[4]
- **Avoid “perclient” DB connections**
- Trigger.dev notes that each WebSocket needing its own query and connection is expensive at scale.[4]
- Instead, centralize DB access in 1N backend services that multiplex changes to many clients.
- **Outbox pattern**
- App writes to main tables and to an **outbox table** in the same transaction.
- A trigger publishes `pg_notify` for new outbox rows.[5]
- A separate process reads/deletes from outbox and broadcasts.
- This keeps writes coherent and avoids doing heavy work inside triggers.[5]
---
## 4. Recommended concrete patterns in 2026ish stack
If you are:
- **Small/medium NestJS app** (≤ 10k concurrent sockets, modest write volume):
- Postgres triggers + `LISTEN/NOTIFY`.
- A NestJS background service (or pg_eventserv) listens and calls a `RealtimeGateway` to broadcast.[3][6]
- Optional: outbox table to decouple triggers from business events.[5]
- **Growing app or highthroughput**:
- Move to **logical replication / CDC** (ElectricSQL, Supabase Realtime, or custom consumer).
- Keep NestJS as the **API + auth + WS edge**, but the “change capture” logic lives in a separate service.[2][4]
- **You want to outsource realtime infra**:
- Supabase Realtime as the WS/event layer on top of Postgres.[2]
- Or Ably/LiveSync with an outbox + NOTIFY bridge from Postgres.[5]
---
If you share your current scale (writes/sec, expected concurrent clients) and whether you can add extra infra (Redis, separate changeconsumer), I can outline an exact NestJS + Postgres + WS architecture and minimal code skeleton tailored to that.
### Sources
- [Source 1](https://www.youtube.com/watch?v=51Mzx0c6Z30)
- [Source 2](https://github.com/supabase/realtime)
- [Source 3](https://www.crunchydata.com/blog/real-time-database-events-with-pg_eventserv)
- [Source 4](https://trigger.dev/blog/how-we-built-realtime)
- [Source 5](https://neon.com/guides/real-time-comments)
- [Source 6](https://dev.to/bias/build-reactive-realtime-apps-with-postgres-49ej)
- [Source 7](https://www.postgresql.org)
- [Source 8](https://www.amplifilabs.com/post/postgres-breakout-year-agent-protocols-rise-and-a-six-bug-rce-wake-up-call)