agentleFS
myatminlumyatminlu/vector-skills/skills/nestjs-dev-guidelines/SKILL.md

nestjs-dev-guidelines

Skill18 starsChanged 3 months agoMITView on GitHub

Production-grade NestJS backend standards for writing, reviewing, and evolving NestJS/Nest-style TypeScript services. Use when a repo has modules, controllers, providers/services, DTOs, guards, pipes, or Nest-oriented boundaries, or when designing NestJS APIs, modules, migrations, auth/RBAC, multi-tenant isolation, DDD layered/hexagonal architecture, ports/adapters, use cases, repository/query patterns, N+1/slow queries, validation, pagination, caching, error handling/exception filters, BullMQ/jobs, webhooks, uploads, decorators, provider scopes, dynamic modules, circular deps, health/readiness/shutdown, observability/logging/tracing, OpenTelemetry, testing, code review, modernization/version/runtime advice, or AI backend patterns like LLM gateways, SSE streaming, usage metering, and quotas. Verify volatile versions/commands/model IDs/APIs against official docs and the repo. Plain Express/Fastify/Hono/Koa without Nest boundaries: cross-cutting guidance only. Not for non-Node backends or pure frontend work.

The file

Show its rules and text

We split this skill into the rules it gives an agent

20 rules. About 0 are common, 13 are distinctive, and 7 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈13

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Before writing or reviewing code, scan references/00-execution-discipline.md and the

    line 16
  2. Very specific:

    When reviewing a PR, run through references/29-code-review-checklist.md + the topic references relevant to the diff.

    line 21
  3. Distinctive:

    Use this section when the repo is "backend TypeScript" but not obviously a standard NestJS app.

    line 26
  4. Very specific:

    Plain Node backends are partial-match only: If the repo is plain Express/Fastify/Hono/Koa without NestJS primitives, use only the …

    line 31
  5. Very specific:

    Follow equivalent-safe repo conventions: If the repo already uses a different but healthy mechanism that preserves the same safety/property …

    line 39
  6. Distinctive:

    Escalate harmful conflicts: If the existing pattern weakens security, correctness, data integrity, or a stable contract, call it out …

    line 43
  7. Very specific:

    Search before writing new code: Check the current module first, then shared/common/core code for an existing util, DTO, service, guard, …

    line 57
  8. Distinctive:

    Fix root causes, not trigger hacks: Do not patch broad behavior with one-off regexes, hardcoded phrases, or manual if/then rules unless the …

    line 60
  9. Distinctive:

    Prefer the smallest correct change: No speculative flags, abstractions, helpers, config, or edge-case handling unless the task or codebase …

    line 63
  10. Distinctive:

    Search and update the full impact surface: If a shared function, contract, DTO, type, or behavior changes, scan and update callers, tests, …

    line 67
  11. Distinctive:

    Ask before changing shared behavior: If reusing existing code requires changing shared semantics, multiple callers, or a reusable contract, …

    line 70
  12. Very specific:

    Never trust the client: Every ID from the URL is checked against the authenticated user/org's ownership.

    line 93
  13. Distinctive:

    One module owns its tables: No cross-module raw DB reads.

    line 98
  14. Distinctive:

    Secrets come from env only — validated with Zod at boot: No secrets in code, no secrets in logs.

    line 117
  15. Distinctive:

    Read the full reference file when you need detail.

    line 168
  16. Distinctive:

    When the user asks you to review a PR, changes, or a diff:

    line 246
  17. Distinctive:

    Read the diff top-to-bottom first — understand intent before critiquing.

    line 248
  18. Very specific:

    If you spot an anti-pattern, link to 30-code-review-anti-patterns.md and quote the good version.

    line 254
  19. Distinctive:

    If the project is an AI product backend (LLMs, agents, RAG, vector store, streaming, usage- based billing), also read:

    line 261
  20. Distinctive:

    Consistency > cleverness: If a repo has an established equivalent-safe pattern that differs from this skill, follow the repo unless the …

    line 277

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

NestJS Dev Guidelines

A complete set of production-grade NestJS and Nest-style Node.js backend standards. Apply these rules whenever working on a codebase that already uses NestJS concepts such as modules, controllers, providers/services, DTOs, pipes, and guards, or when the task is explicitly about designing those patterns. Think like a senior backend engineer: consistency over cleverness, explicit over implicit, boundaries over shortcuts.

How to use this skill

  1. Before writing or reviewing code, scan references/00-execution-discipline.md and the Non-Negotiables below.
  2. For file placement, module boundaries, or refactor decisions, use the Decision Trees.
  3. For deep detail on a topic, open the matching references/NN-<topic>.md file. Each is self-contained: TL;DR, rules, good/bad examples, anti-patterns, and a review checklist.
  4. When reviewing a PR, run through references/29-code-review-checklist.md + the topic references relevant to the diff.

Scope and precedence

Use this section when the repo is "backend TypeScript" but not obviously a standard NestJS app.

  1. NestJS-first scope. Apply this skill directly when the repo already uses NestJS primitives (@Module, controllers, providers/services, guards, pipes, DTOs) or when the user is asking you to design a NestJS solution.
  2. Plain Node backends are partial-match only. If the repo is plain Express/Fastify/Hono/Koa without NestJS primitives, use only the cross-cutting guidance here (validation, contracts, authz, DB design, testing, observability). Do not force Nest-specific APIs, decorators, or folder structure onto that codebase.
  3. Protect outcomes first, tooling second. The non-negotiables below are invariants first and implementation defaults second. Centralized input validation, stable response contracts, structured redacted logging, ownership checks, and test coverage matter more than the exact library used to achieve them.
  4. Follow equivalent-safe repo conventions. If the repo already uses a different but healthy mechanism that preserves the same safety/property boundary (for example Zod instead of class-validator, or a repo-standard structured logger instead of nestjs-pino), follow the repo rather than rewriting it mid-task.
  5. Escalate harmful conflicts. If the existing pattern weakens security, correctness, data integrity, or a stable contract, call it out explicitly and ask before normalizing or widening that pattern.

Execution discipline

Apply these rules before touching NestJS-specific details. They exist to reduce the usual LLM failure modes: silent assumptions, overbuilt code, broad refactors, and unverified fixes.

  1. Think before coding. State assumptions. If the task is ambiguous, ask or explicitly list the plausible interpretations instead of silently picking one.
  2. Verify volatile facts. Versions, package APIs, model IDs, install commands, and CLI flags change. Verify them against official docs and the repo before recommending — do not recall from memory. See 35-source-of-truth-freshness.md.
  3. Search before writing new code. Check the current module first, then shared/common/core code for an existing util, DTO, service, guard, interceptor, repository, or pattern you can reuse.
  4. Fix root causes, not trigger hacks. Do not patch broad behavior with one-off regexes, hardcoded phrases, or manual if/then rules unless the product requirement is literally deterministic rule-based routing.
  5. Prefer the smallest correct change. No speculative flags, abstractions, helpers, config, or edge-case handling unless the task or codebase clearly needs them.
  6. Make surgical edits. Touch only the lines needed for the request. Do not clean up unrelated code, comments, or formatting just because you are nearby.
  7. Search and update the full impact surface. If a shared function, contract, DTO, type, or behavior changes, scan and update callers, tests, docs, examples, and related flows until the whole change is consistent.
  8. Ask before changing shared behavior. If reusing existing code requires changing shared semantics, multiple callers, or a reusable contract, stop and confirm instead of silently widening the blast radius.
  9. Define what success looks like. Convert vague requests into checks you can verify: regression test, unit test, e2e test, typecheck, lint, build, or a concrete manual check.
  10. Stop when confused. Name the uncertainty early. Short clarifying questions are cheaper than rewriting the wrong code.

Open references/00-execution-discipline.md for the full checklist and examples.

Non-negotiables (protect these outcomes; prefer these patterns)

Each rule has a Why so you can reason about edge cases instead of applying it blindly.

  1. No business logic in a controller. Controllers validate input, delegate to a service, and shape the response. Nothing else. Why: controllers are thin HTTP adapters. Logic in them can't be unit-tested without booting the framework, and it pulls HTTP concerns into domain code. See 04-code-quality.md.
  2. Every external input goes through a DTO. Body, query, and param DTOs use class-validator; ValidationPipe is global with whitelist: true, forbidNonWhitelisted: true, transform: true. Why: DTOs are the one choke point where unknown fields, bad types, and injection payloads are stopped. Skipping one means you trust the client. See 09-validation.md.
  3. Never trust the client. Every ID from the URL is checked against the authenticated user/org's ownership. Filter and sort fields are whitelisted. Mass-assignment is prevented by whitelist. Why: IDOR and mass-assignment are the two most common application-level breaches. They only exist when code assumes "if the token is valid, the payload is fine." See 11, 12.
  4. One module owns its tables. No cross-module raw DB reads. If module B needs data from module A, call A's service (DI) or subscribe to A's events. Why: shared table access makes every schema change a cross-team coordination problem. Modules become coupled through the DB instead of through APIs. See 03-module-design.md.
  5. snake_case in the database, camelCase in code. Tables plural, columns snake_case, primary keys id, foreign keys <entity>_id. Why: each ecosystem has a convention; mixing them creates a lifetime of mapping bugs and makes ad-hoc SQL painful. Pick the convention of the side that's hardest to change (the DB). See 13-database-design.md.
  6. Responses follow one stable contract. Single-resource success returns the object itself; list success returns { data, meta }; errors return { code, message, details?, traceId } with the correct HTTP status. Why: a consistent contract lets clients write one error handler and one pagination handler that works everywhere, and lets support triage issues by traceId. See 07, 10.
  7. Pagination is required for any list endpoint. Choose cursor/keyset for sequential browsing over mutable or large data; choose offset when page-number navigation or exact totals are real product requirements. Both return meta with pagination info. Why: an unpaginated list is a latent OOM and a latent DB outage. The right pagination model depends on UX, consistency requirements, and scale. See 08.
  8. Secrets come from env only — validated with Zod at boot. No secrets in code, no secrets in logs. Invalid env = crash before serving traffic. Why: a missing/malformed env var caught at boot is a minor incident; caught at runtime on the hot path, it's a customer outage. See 20, 11.
  9. Structured logs, redacted. nestjs-pino with JSON in prod; redact authorization, cookie, set-cookie, password, token. Correlation ID on every log line. Why: plain-text logs can't be queried at scale, and one unredacted Authorization header is a credential leak with a long tail. See 21.
  10. Test at boundaries, not internals. Unit-test services with mocked dependencies; e2e-test controllers through the HTTP layer; never mock the class under test. Why: mocking the code under test just re-asserts the mock. Tests should verify the contract (HTTP, DB, external calls), not the implementation. See 23.
  11. Tenant identity is server-derived and layered. In a multi-tenant app, tenant/org id comes from the authenticated session/JWT, never from the request body. Guards, services, and repositories each filter by tenant; tests prove tenant A cannot see tenant B's data. Why: cross-tenant leaks almost always come from a single missing WHERE tenant_id = ? or a client-supplied id that was trusted too early. Layered enforcement means a bug in one layer does not leak data. See 33.

Senior-engineer mindset (decision trees)

Full decision trees live in references/05-thinking-decision-trees.md. Short version:

Where does this file go?

  • Business feature → modules/<feature>/
  • App-wide infra (auth, redis, mail) → core/<name>/
  • Generic utility (pipe, decorator, type) → common/<kind>/
  • External API client (Stripe, AWS) → integrations/<provider>/
  • CLI/CRON job → commands/<name>.command.ts
  • Domain event publisher/listener → events/<event-name>/

Should I create a new module?

  • Does it own its own DB tables? → Yes, new module.
  • Does it have its own lifecycle / domain logic? → Yes, new module.
  • Is it just a helper used by one module? → No, put it inside that module's utils/.
  • Is it used by 2+ modules but owns no data? → common/ utility.

Should I refactor this now?

  • Am I already editing this code? → Yes, clean as you go.
  • Is it blocking my current feature? → Yes, refactor minimally.
  • Is it just "ugly"? → No, note it and move on.

Should I skip the test?

  • Is it a controller? → No. E2E test always.
  • Is it a service with branching logic? → No. Unit test always.
  • Is it a thin pass-through (e.g., findById) with no logic? → Skipping is OK; the e2e test of the controller covers it.
  • Is it a DTO / type? → No test needed; the compiler is the test.

Rule index (one line per reference)

Read the full reference file when you need detail. The number prefix is for stable ordering.

#FileRule in one line
0000-execution-discipline.mdThink first, verify volatile facts (versions/APIs/model IDs) against docs, keep changes small, edit surgically, define success criteria, verify before claiming done
0101-folder-structure.mdsrc/{core,common,integrations,modules,events,commands} — one place for each kind of code
0202-naming-conventions.mdcamelCase vars, PascalCase classes, snake_case DB, kebab-case.ts files, SCREAMING_SNAKE env
0303-module-design.mdOne module per bounded context; @Global() only for true app-wide infra
0404-code-quality.mdSOLID, constructor DI, pure utils, small functions, no any without a reason
0505-thinking-decision-trees.mdHow to decide: where to put code, when to refactor, when to skip a test
0606-api-design.mdREST, plural nouns, verbs match semantics, URI versioning /v1/..., idempotency keys
0707-standard-responses.mdSingle success returns a plain object; lists return { data, meta }; errors return { code, message, details?, traceId }
0808-pagination-filters-sorting.mdCursor/keyset for sequential browsing, offset when page numbers/exact totals are real requirements; filter[field]=, sort=-createdAt; whitelist fields
0909-validation.mdclass-validator DTOs + global ValidationPipe; Zod for env + runtime JSON parsing
1010-error-handling.mdHybrid taxonomy: HTTP status + namespaced code + traceId; domain errors extend semantic Nest exceptions; one global filter with host.getType() + headersSent guards, logs via PinoLogger
1111-security.mdSecurity review routine: OWASP Top 10, transport/CORS, injection/SSRF, password hashing, rate limits, PII/audit, and links to auth/webhooks/uploads
1212-authentication-patterns.mdSession cookie (browsers) or Bearer JWT (mobile/server); hash session/refresh tokens at rest; rotate refresh; use revoked_before; cookie takes precedence and invalid cookies fail closed; auth errors use { code, message }
1313-database-design.mdsnake_case, plural tables, FK <entity>_id, indexes on FKs + query paths, deleted_at, UUIDv7 or bigint
1414-database-orm-patterns.mdraw pg / TypeORM / Prisma / Drizzle — side-by-side patterns
1515-migrations.mdAlways forward-only in prod; no destructive changes without two-step rollout
1616-cascade-rules.mdON DELETE CASCADE for owned data; RESTRICT for shared refs; SET NULL for optional
1717-pipelines-interceptors-guards.mdOrder: Guard → Interceptor (pre) → Pipe → Handler → Interceptor (post) → Filter
1818-events.mdEventEmitter2 for in-process; outbox pattern when crossing services or queues
1919-background-jobs.mdBullMQ default; idempotent handlers; retries with backoff; DLQ for poison messages
2020-configuration.mdConfigModule global; Zod schema; fail fast on boot if env invalid
2121-logging.mdnestjs-pino, JSON in prod, redact secrets, correlation ID per request
2222-observability.mdOpenTelemetry traces + metrics; Langfuse/Helicone for LLM traces
2323-testing.mdUnit beside impl (*.spec.ts); e2e in test/; mock at boundaries; real DB for integration
2424-performance.mdAvoid N+1; size the pool; cache selectively; stream large payloads
24a24a-caching-patterns.mdCache deliberately; stable namespaced keys, TTL + invalidation, stampede protection; never the sole authority for auth/quota/billing
2525-documentation-swagger.md@ApiTags / @ApiOperation / @ApiResponse; DTOs auto-schema via @ApiProperty
2626-ai-product-patterns.mdLLM gateway with provider abstraction, retry, fallback, timeout
2727-ai-streaming-sse.mdSSE endpoints; cancel-aware (abort upstream); heartbeat; typed event vocab; not resumable on reconnect
2828-ai-usage-metering-cost.mdPer-call token + cost rows; aggregate per user/org/model; enforce quotas
2929-code-review-checklist.mdPR review checklist across all rules above
3030-code-review-anti-patterns.mdCatalog of anti-patterns with good-vs-bad snippets
3131-rules-rationale-examples.mdCross-cut rule + rationale + good/bad examples for quick reference
3232-modern-nestjs-stack.mdDecision checklist for modernizing/starting a NestJS service; bootstrap order, module-system checks; no frozen version matrix
3333-multi-tenancy-patterns.mdServer-derived tenant identity enforced across auth, guard, service, and repository layers; tests prove isolation
3434-health-shutdown.mdLiveness vs readiness; one shutdown coordinator; drain before close; worker processes drain separately
3535-source-of-truth-freshness.mdDurable invariants stay local; volatile APIs/versions/models verified against official docs and the repo
3636-webhooks.mdVerify signature on raw bytes (raw-body config + timingSafeEqual), dedupe on (provider, event_id), ack 2xx after enqueue (incl. unhandled types), re-fetch authoritative state for high-stakes events, resolve tenant from the verified payload
3737-file-uploads.mdPrefer presigned direct-to-bucket uploads (PUT for clients, POST policy for browsers); cap size/MIME at the boundary; sniff magic bytes; opaque tenant-prefixed storage keys; server-compute hash/size/mime; AV scan before exposure; rate-limit upload endpoints
3838-decorators-scopes-dynamic-modules.mdParam decorators only extract from request; default to singleton scope; dynamic modules for configurable infra; forwardRef is a smell
3939-exception-filters.mdOne global filter shapes every error to { code, message, details?, traceId }; throw typed HttpException subclasses; never leak internals
4040-ddd-layered-architecture.mdOptional DDD layering in three tiers (classic layers → layered feature modules → hexagonal ports/adapters); dependencies point inward; domain stays framework-free; default six-bucket layout still wins for CRUD
4141-n-plus-one-elimination.mdProve N+1 by counting queries (never by guessing); six shapes beyond the obvious loop; JOIN vs two-query batch-load vs per-request DataLoader; never SQL LIMIT over a joined collection; lock the fix with a query-count test

When to deep-read

You are doing...Open...
Starting any implementation or bug fix00, 05
Starting a new feature module01, 03, 04
Structuring with DDD layers / hexagonal / ports-adapters40, 01, 03
Starting or modernizing a service32, 34, 20
Designing a new endpoint06, 07, 08, 09
Returning errors consistently10
Designing DB tables13, 14, 15, 16
Adding auth to an endpoint11, 12, 17
Adding multi-tenant isolation33, 11, 12, 14
Adding a list endpoint07, 08, 41
Adding caching24, 24a
Fixing a slow endpoint, an N+1, or high query counts41, 24, 14, 13
Adding a background task19, 34
Writing tests23
Adding observability21, 22
Adding health/readiness/shutdown34, 32
Building an LLM feature26, 27, 28, 22
Handling webhooks from a third party36, 11, 19, 21, 33
Accepting file uploads37, 11, 24
Designing custom decorators / scoped providers / dynamic modules38, 03, 17
Designing the global exception filter / error wire shape39, 07, 10
Giving version/command/model advice35
Reviewing a PR29, 30, and any topic relevant to the diff

Code review mode

When the user asks you to review a PR, changes, or a diff:

  1. Read the diff top-to-bottom first — understand intent before critiquing.
  2. Walk the 29-code-review-checklist.md — mark each item pass/fail/NA.
  3. For each fail, cite the specific section in the relevant reference file (e.g., "business logic in controller — see 04-code-quality.md → Layering / Anti-patterns").
  4. Distinguish blockers (security, data-loss risk, non-negotiable rule broken) from suggestions (style, minor naming, optional refactor). Don't block on suggestions.
  5. If you spot an anti-pattern, link to 30-code-review-anti-patterns.md and quote the good version.
  6. End the review with: (a) blockers (numbered), (b) suggestions (bulleted), (c) approval conditional on blockers being resolved.

AI product appendix

If the project is an AI product backend (LLMs, agents, RAG, vector store, streaming, usage- based billing), also read:

  • 26-ai-product-patterns.md — provider-agnostic LLM gateway, retry/fallback
  • 27-ai-streaming-sse.md — SSE endpoint patterns, cancel, heartbeat
  • 28-ai-usage-metering-cost.md — token + cost metering, quota enforcement
  • 22-observability.md — Langfuse/Helicone for LLM tracing

Everything else (auth, DB, error handling, testing) applies identically to AI products — the AI bit is a module, not a framework.

Meta

  • Execution discipline before framework detail. Small, explicit, verified changes beat fast, broad, clever ones.
  • Rules > style preferences. These are rules because they prevent real bugs or pay back in maintainability. Style debates are not covered.
  • Consistency > cleverness. If a repo has an established equivalent-safe pattern that differs from this skill, follow the repo unless the pattern is actively harmful.
  • When in doubt, keep it boring. Boring code is easy to review, easy to onboard, and easy to replace. That is the point.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.