Claude
Skills
Sign in
Back

generating-nest-servers

Included with Lifetime
$97 forever

Handles ALL NestJS and @lenne.tech/nest-server development tasks including module creation, service implementation, controller/resolver development, model definition, and debugging. Covers lt server commands, @Roles/@Restricted security, CrudService patterns, and API tests. Supports monorepos (projects/api/, packages/api/). Activates when working with src/server/ files, NestJS modules, services, controllers, resolvers, models, DTOs, guards, decorators, or REST/GraphQL endpoints. NOT for Vue/Nuxt frontend (use developing-lt-frontend). NOT for nest-server version updates (use nest-server-updating). NOT for TDD workflow orchestration (use building-stories-with-tdd).

Web Dev

What this skill does


# NestJS Server Development Expert

## Gotchas

- **`declare` keyword on Model properties kills decorators** — `declare name: string` tells TypeScript "this property exists but isn't emitted at runtime" — which means Mongoose/Typegoose decorators (`@Prop`, `@Field`) attached to it are lost. Always use `name!: string` (definite assignment) or `name: string = ''` (default value) on model fields. Tests pass silently in memory but production MongoDB writes drop the field.
- **`securityCheck()` returning `this` unchanged is a red flag** — A trivial `return this;` implementation means no permission boundary exists on the model. The decorator system requires `@Restricted`/`@Roles` + an actual check (usually filtering sensitive fields or verifying ownership). If the model genuinely has nothing to restrict, add a comment explaining why — otherwise it will be flagged in every security review.
- **`--controller` flag generates REST — even in GraphQL projects** — `lt server object <name> --controller` produces a REST controller by default. For GraphQL projects, use `--resolver` instead. The CLI does not auto-detect from the project's existing pattern.
- **`CrudService` is the default — always extend it** — New modules often get hand-rolled service classes instead of `extends CrudService`. The framework's pagination, filtering, population, and permission integration all depend on CrudService inheritance. Hand-rolled services break `/api/<entity>/find` queries silently for consumers.
- **Alphabetical property order is enforced in reviews** — Class fields, DTO properties, and model definitions are reviewed for alphabetical ordering. Generated code from `lt server` usually respects this; hand-edited additions often break it. Run a final pass before committing.

## Ecosystem Context

Developers typically work in a **Lerna fullstack monorepo** created via `lt fullstack init`:

```
project/
├── projects/
│   ├── api/    ← nest-server-starter (depends on @lenne.tech/nest-server)
│   └── app/    ← nuxt-base-starter (depends on @lenne.tech/nuxt-extensions)
├── lerna.json
└── package.json (workspaces: ["projects/*"])
```

**Package relationships:**
- **nest-server-starter** (template) → depends on **@lenne.tech/nest-server** (core package)
- **nuxt-base-starter** (template) → depends on **@lenne.tech/nuxt-extensions** → aligned with nest-server
- This skill covers `projects/api/` and any code depending on `@lenne.tech/nest-server`

## When to Use This Skill

- Creating/modifying NestJS modules, services, controllers, resolvers, models
- Running/debugging the NestJS server (`pnpm start`, `pnpm run dev`, `pnpm test`)
- Using `lt server module`, `lt server object`, `lt server addProp`, `lt server create`
- Creating API tests for controllers/resolvers
- Analyzing existing NestJS code, architecture, relationships
- Answering NestJS/@lenne.tech/nest-server questions

**Rule: If it involves NestJS or @lenne.tech/nest-server in ANY way, use this skill!**

## Skill Boundaries

| User Intent | Correct Skill |
|------------|---------------|
| "Create a NestJS module" | **THIS SKILL** |
| "Debug a service error" | **THIS SKILL** |
| "Add a REST endpoint" | **THIS SKILL** |
| "Add an AI tool / chat endpoint / MCP server" (nest-server ≥ 11.26.0) | **THIS SKILL** — see `reference/ai-module-integration.md` + `reference/mcp-integration.md` |
| "Update nest-server to v14" | nest-server-updating |
| "Write tests first, then implement" | building-stories-with-tdd |
| "Update npm packages" | maintaining-npm-packages |
| "Build a Vue component" | developing-lt-frontend |
| "Run lt fullstack init" | using-lt-cli |

## Related Skills & Commands

- `developing-lt-frontend` - For ALL Nuxt/Vue frontend development (projects/app/)
- `building-stories-with-tdd` - For TDD workflow (tests first, then implementation)
- `using-lt-cli` - For Git operations and Fullstack initialization
- `nest-server-updating` - For updating @lenne.tech/nest-server versions
- `contributing-to-lt-framework` - When modifying `@lenne.tech/nest-server` itself and testing via `pnpm link`
- `/lt-dev:review` - General security review of branch diff
- `/lt-dev:backend:sec-review` - Security review after implementing endpoints or modifying auth/authz
- `/lt-dev:backend:sec-audit` - Full OWASP security audit for dependencies, config, and code

**In monorepo projects:**
- `projects/api/` or `packages/api/` → This skill
- `projects/app/` or `packages/app/` → `developing-lt-frontend`

## Dev Server Lifecycle

When starting the API for manual testing, debugging, or E2E tests: **prefer `lt dev up`** over `nest start` / `pnpm dev` directly. `lt dev up` serves the API under a stable HTTPS URL (`https://api.<slug>.localhost`) via Caddy, sets `BASE_URL`/`APP_URL`/`NSC__MONGOOSE__URI` automatically, and detaches into `<root>/.lt-dev/api.log`. Stop with `lt dev down`. For non-lt-projects (or when explicitly requested): use `run_in_background: true` and `pkill -f "nest start"` afterwards. Leaving dev servers orphaned blocks the Claude Code session ("Unfurling..."). Full rules: `managing-dev-servers` skill.

## CRITICAL RULES

### CLI-First Development

When creating new modules, objects, or adding properties, **use `lt server` CLI commands first** before writing code manually. The CLI generates complete, standards-compliant scaffolding with all decorators, imports, and module integration.

```bash
# Always add --noConfirm --skipLint for non-interactive execution
lt server module --name Product --controller Rest --noConfirm --skipLint \
  --prop-name-0 name --prop-type-0 string \
  --prop-name-1 price --prop-type-1 number

lt server object --name Address --noConfirm --skipLint \
  --prop-name-0 city --prop-type-0 string

lt server addProp --type Module --element User --noConfirm --skipLint \
  --prop-name-0 avatar --prop-type-0 string --prop-nullable-0 true
```

**After CLI scaffolding**, customize the generated code: business logic, security rules (`securityCheck`), descriptions, and custom methods.

**Complete flag reference: [reference/configuration.md](${CLAUDE_SKILL_DIR}/reference/configuration.md#property-flags-reference)**

### Security (NON-NEGOTIABLE)

1. **NEVER** remove/weaken `@Restricted()` or `@Roles()` decorators
2. **NEVER** modify `securityCheck()` to bypass security
3. **ALWAYS** analyze permissions BEFORE writing tests
4. **ALWAYS** test with the LEAST privileged authorized user
5. **VERIFY** decorator coverage with `lt server permissions` after creating modules

**Complete security rules: [reference/security-rules.md](${CLAUDE_SKILL_DIR}/reference/security-rules.md)** | **OWASP checklist: [reference/owasp-checklist.md](${CLAUDE_SKILL_DIR}/reference/owasp-checklist.md)**

### Prefer CrudService Over Direct Model Access (Informed-Trade-off Pattern — Rule 14)

**Prefer CrudService methods over direct access to your own Mongoose Model** (`this.mainDbModel.xxx` / `this.<modelName>Model.xxx` inside the owning Service). Direct own-Model access is an instance of the [Informed-Trade-off Pattern](${CLAUDE_SKILL_DIR}/reference/informed-trade-off-pattern.md) (see also `reference/security-rules.md` Rule 14): allowed with a good reason, but every use must be analyzed for unintentionally bypassed processes, skipped authorization, or missing side-effects.

```typescript
// AVOID - Direct model access bypasses security and permissions
const product = await this.productModel.findOne({ _id: id });
const users = await this.mainDbModel.find({ active: true });
await this.orderModel.updateOne({ _id: id }, { status: 'done' });

// PREFERRED - CrudService methods handle security, permissions, population
const product = await this.findOne({ id }, serviceOptions);
const users = await this.find({ filterQuery: { active: true }, currentUser });
await this.update(id, input, serviceOptions);
await this.userService.findOne({ id: userId }, { currentUser });
```

**Why CrudService first:**
- `checkRestricted()` enforces field-level `@Restricted` permissions 

Related in Web Dev