NestJS GraphQL Structure
Where every file belongs in a NestJS GraphQL (code-first) application, and how each file works. This skill is a placement and anatomy guide: it answers "which folder does this file go in?", "what is this file responsible for?", and "how do modules connect to resolve fields and relations?". It covers the base structure map plus per-file rules for modules, services, resolvers (query, mutation, subscription, and field), inputs and args, pagination, entities (Mongoose schema best practices for shared types), repository-only model injection, models/mappers, repositories, Mongoose discriminators, derived fields, providers, loaders, events, background jobs, auth, exceptions, custom scalars, config, common building blocks, infra setup, and the naming/testing/lint conventions.
It is deliberately separate from the nestjs-best-practices skill. That skill covers framework behavior (dependency-injection depth, security, performance, error handling, microservices, DevOps); this one covers structure and module composition. Where they overlap (feature modules, circular dependencies), this skill looks at it through a placement lens and cross-references the other skill for behavioral depth.
This skill covers both structure rules (where a file lives) and module-resolution rules (how modules connect at runtime — e.g. circular-dependency resolution). Module-resolution rules live in Sections 2 and 3.
When to Apply
Reference these guidelines when:
- Deciding where a new file belongs in a NestJS GraphQL codebase
- Designing or reviewing a feature-first directory layout
- Scaffolding a new feature module and its files
- Deciding how to model polymorphic documents (Mongoose discriminators) and publish their GraphQL type automatically
- Deciding where a Mongoose model may be injected — and keeping database access behind a repository
- Deciding whether a value belongs in the database or should be derived when read
- Working out how a module resolves a field or relation that lives in another module
- Resolving connection problems between modules (circular dependencies, missing exports)
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Application Structure | CRITICAL | struct- |
| 2 | Modules & Bootstrap | CRITICAL | module- |
| 3 | Relations & Cross-Module | CRITICAL | relations- |
| 4 | Services | HIGH | service- |
| 5 | GraphQL Resolvers | HIGH | resolver- |
| 6 | GraphQL Types & Inputs | HIGH | type- |
| 7 | Data Access | MEDIUM-HIGH | data- |
| 8 | Config, Common & Infra | MEDIUM | infra- |
| 9 | Conventions & Testing | MEDIUM | conventions- |
Quick Reference
1. Application Structure (CRITICAL)
struct-auth-feature- Keep Authentication in Its Own Feature Modulestruct-canonical-layout- Canonical Application Structurestruct-feature-decomposition- Split a Feature into Sub-Modules When It Growsstruct-file-placement-table- File Placement Tablestruct-module-kinds- Distinguish Domain Features from App-Level Modulesstruct-schema-artifact- Locate and Policy the Generated Schema Artifact
2. Modules & Bootstrap (CRITICAL)
module-bootstrap- Keep main.ts and app.module.ts Thinmodule-export-policy- Export Only the Feature's Public API (Its Services)module-feature-module- The Feature Module File
3. Relations & Cross-Module (CRITICAL)
relations-avoid-circular- Resolve Circular Module Dependenciesrelations-dataloader- Batch Relations with a Per-Request DataLoaderrelations-events- Decouple Modules with Eventsrelations-field-resolvers- Resolve Relations with @ResolveFieldrelations-owning-module- Put a Relation Field in the Module That Owns Its Data
4. Services (HIGH)
service-background-jobs- Keep Queue Processors and Scheduled Jobs with the Featureservice-external-providers- Keep Third-Party Clients in providers/service-interfaces-tokens- Declare Service Contracts and Injection Tokens in interfaces/service-single-responsibility- Multiple Focused Services per Moduleservice-small-focused-methods- Keep Service Methods Small and Focused
5. GraphQL Resolvers (HIGH)
resolver-computed-fields- Declare Fields on the Type; Resolve Computed Ones in the Resolverresolver-organization- Split Resolvers by Role into resolvers/resolver-per-type- One Resolver File per Type That Has Field Resolversresolver-subscriptions- Put Subscription Resolvers in resolvers/ with PubSub in infra/
6. GraphQL Types & Inputs (HIGH)
type-args- Define @Args Classes in dto/type-inputs- Define GraphQL Inputs in dto/type-pagination- Paginate List Queries with a Shared Page Typetype-polymorphic-discriminators- Publish Discriminated Documents as an Interface or Uniontype-unions-interfaces- Place Union and Interface Types with the Module's Types
7. Data Access (MEDIUM-HIGH)
data-derived-fields- Compute Derived Fields; Do Not Store Themdata-discriminators- Model Polymorphic Subtypes with Discriminatorsdata-entity-mongodb- Place MongoDB Entities in entities/data-model-injection- Inject a Model Only in the Repositorydata-models-mappers- Split models/ and mappers/ When API Differs From Storagedata-mongoose-schema- Mongoose Schema Best Practices for Shared Typesdata-repository- Keep Data Access in repositories/
8. Config, Common & Infra (MEDIUM)
infra-common-types- Put Shared Types in common/types/infra-common- Put Shared Building Blocks in common/infra-config- Put Typed Configuration in config/infra-custom-scalars- Implement Custom Scalars in common/scalars/infra-database- Put Database Setup and Migrations in infra/database/infra-directives- Put Schema Directives in common/directives/infra-exceptions- Put Exceptions in exceptions/infra-graphql- Put GraphQL Setup in infra/graphql/infra-seeds- Put Seed Scripts in infra/database/seeds/infra-shared-module- Use a SharedModule for Cross-Cutting Services
9. Conventions & Testing (MEDIUM)
conventions-barrels-aliases- Use Barrels and Path Aliases Deliberatelyconventions-cli-generator- Use the Nest CLI Generator, Then Adjustconventions-constants-enums- Keep Constants and Enums Out of Class Filesconventions-module-boundaries- Enforce Module Boundaries with Lintconventions-naming- File and Class Naming Conventionsconventions-test-fixtures- Place Test Fixtures, Factories, and Helpers in test/conventions-testing- Test File Placement
How to Use
Start with the base structure rule, then read the rule for the specific file kind you are
working on. Rule files live under rules/:
rules/struct-canonical-layout.md
rules/_sections.md
Each rule file contains:
- Purpose and placement (where the file lives and how it is named)
- Responsibilities and anti-responsibilities
- An incorrect example with an explanation
- A correct example with an explanation
Full Compiled Document
For the complete guide with all rules expanded in a single document, see AGENTS.md.