elsikora/nestjs-crud-automator/crud-automator

Claude Code and agent skill by elsikora/nestjs-crud-automator

Trust tier 2
  • community
  • ext:[{"type":"hook","description":"Pre/post lifecycle hooks"}]
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Design, audit, document, refactor, and implement NestJS CRUD Automator resources using @elsikora/nestjs-crud-automator. Use when working with ApiController, ApiService, ApiFunction, ApiFunctionCustom, ApiRouteCustom, ApiMethod, ApiPropertyDescribe, ApiPropertyCopy, manual DTOs, autoDto, GET_LIST response item DTOs, transformers, validators, relation loading, subscribers, authorization policies, HOOKS/IAM, transaction scopes, Swagger contracts, or replacing hand-written NestJS CRUD patterns with…

Install

npx vskill@latest install elsikora/nestjs-crud-automator/crud-automator

Semi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard

SKILL.md

Crud Automator

Source Priority

Use local source as the contract:

  1. src/interface/**, src/type/**, and exported barrels define public API shape.
  2. test/unit/** and test/e2e/** show supported behavior.
  3. docs/** and README.md explain behavior, but may drift and must be checked against source.

Default Workflow

  1. Inspect the existing entity, service, controller, subscriber, policy, and DTO configuration.
  2. Prefer native Crud Automator primitives before adding custom controllers, mappers, facades, or wrappers.
  3. Verify route config against current interfaces before copying examples.
  4. Validate Swagger, DTO shape, relation loading, subscriber firing, authorization decisions, and transaction behavior after meaningful changes.

Current Contract Reminders

  • @ApiController() applies Nest @Controller() internally; use path.
  • routes: {} is valid and still generates default CRUD routes; omitted route entries do not disable routes.
  • Generated controller routes are CREATE, GET, GET_LIST, UPDATE (PUT /:id), PARTIAL_UPDATE (PATCH /:id), and DELETE.
  • GET_MANY is service/function/subscriber-only, not a generated HTTP route.
  • Route controls live under generation: generation.isEnabled, generation.shouldWriteToController, and generation.decorators.
  • Route security lives under security.authentication and security.authorization.
  • authentication.type is the principal category (USER, ADMIN, ACCOUNT, MERCHANT, or project string), not the bearer/security scheme.
  • Route-level Swagger security schemes use authentication.securityRequirements; one object is an AND group and multiple objects are OR alternatives.
  • Request config is target keyed with EApiControllerRequestTarget.BODY, PARAMETERS, and QUERY.
  • Response config is target keyed with EApiControllerResponseTarget.RESPONSE.
  • OpenAPI response headers live directly under response.headers, not under EApiControllerResponseTarget.RESPONSE.
  • Generated routes declare exactly the error statuses they can return: 400 and 500 always, 404 on row routes and on CREATE with relations.request (a securable CREATE outside the policy scope answers 403, which the securable controller declares), 409 on CREATE and updates, 413 and 415 on every route (Express parses a GET body, Fastify a DELETE body), 401 and 403 with any guard (security.authentication, execution.guards, @UseGuards on the class or the route, also on a REQUEST-scoped subclass without its own @ApiController, not APP_GUARD), 403 with a securable controller with authorization, plus each validator exception status, also one HttpStatus does not list. GET_LIST never declares 404. Custom routes (@ApiRouteCustom, @ApiMethod) get 401/403 by the same rule, 429 with throttling.default, 400/413/415/500 on every route, each validator exception status, and 404 with an enabled relation in relations.request.load.include or any key in relations.response.load.include, also one set to false; what a handler throws comes from response.errors. Application-owned statuses such as a global ThrottlerGuard 429 go in documentation.response.statuses, which replaces the default response of that status. Every status requires only statusCode and message.
  • autoDto is validators-only. Entity ApiPropertyDescribe({ properties }) remains the capability baseline; generated GET_LIST request[QUERY].filter, order, and pagination own its generated query contract.
  • ApiPropertyDescribe({ isAutoDtoEnabled: false }) keeps entity metadata, TypeORM behavior, and manual DTO use but removes the property from every generated BODY, PARAMETERS, QUERY, and RESPONSE DTO, generated Swagger relation components, and metadata-driven or typed client filter/order input. Omitted or true preserves normal generation.
  • Route/DTO isEnabled: true, generated GET identity, pathScope, rowPath, and query plans cannot re-enable a globally hidden property. Explicit ApiPropertyCopy may deliberately copy it to a manual DTO, but all other route/DTO, guard, and validation rules still apply. PAGE server-only defaults/tie-breakers may use the described scalar; CURSOR ordering cannot because it requires generated response exposure.
  • Manual dto and autoDto route branches are mutually exclusive.
  • A manual GET_LIST QUERY DTO is mutually exclusive with generated filter/order/pagination configuration. Manual RESPONSE DTOs remain compatible; CURSOR additionally requires Automator metadata proving its exact flat wrapper and same-name raw protected item fields.
  • Generated GET accepts top-level identity: { parameter: "gameId" } to rename only its primary-key wire parameter. The service predicate and HOOKS/IAM canonical identity still use the actual primary field, while the response shape is unchanged. It aliases one path field: more than one row column fails at startup. A controller path with parameters needs a complete controller-level pathScope.
  • Generated row routes (GET, PATCH, PUT, DELETE) take every primary key column in declaration order. Controller-level rowPath: { fields } chooses the columns and their order; empty, unknown, non-key, repeated, or pathScope-bound columns fail at startup. A date and time key column in the path of a composite key must be timestamptz with precision 0 to 3, or datetime with no precision or precision 0 to 3 (SQLite, MySQL, MariaDB), and takes only the canonical UTC millisecond form; a single-column key without bindings keeps its route byte for byte.
  • Controller-level pathScope: { parameters: [{ parameter, field }] } binds controller-path parameters on every generated route. With any generated route enabled, every path parameter must be bound exactly once to a distinct described direct scalar field, and wildcard or optional parameters fail at startup. A controller with all six generated routes disabled keeps any path. There is no route-level read key.
  • pathScope and rowPath are mutually exclusive with a manual PARAMETERS DTO on a route. They create route-scoped <Prefix><Route>ParametersDTO components. Bound fields leave generated bodies; CREATE takes the value from the path and answers 400 for a body naming another one, relation keys included; writes outside the binding answer 404. A PATCH or PUT body key column must equal its row parameter, and a key column the path leaves out cannot appear; the route checks the body as sent (copied before subscribers and transformers) and the processed body, so no pipe or transformer changes this. A body key or bound value must be the string the path carries (canonical UTC millisecond form for a date and time), and a matching one is dropped from a copy before the write, the request body unchanged. A relation whose join columns the entity declares, over one column or several, is written through the entity's own columns on every generated write, path or not: the own value (path, else body after transformers, else the raw value a pipe dropped) wins, a differing relation value answers 400, a join column the body omits takes the value of the relation the route writes (after subscribers and transformers; a relation they or a pipe removed sets nothing, the sent copy is only compared), each join column must be a single value (an object or array there answers 400), and PATCH/PUT keep key columns. A relation over one column is its referenced column's value (a Date from a transforming pipe included) or an object naming it; request relation loading still sees it, then a pure reference (a value, or an object of only the referenced and key columns) leaves the saved body unless loading replaced it with the row, so only the own column is written, while an object with other values of the related row stays for a cascade; an object naming no join column answers 400; null for a key column answers 400. A relation over several columns must be an object when the entity or path sets one of its columns (null, a scalar or an array answers 400; clear it with a non-key own column such as parentId: null, or the reason says it cannot be cleared); a date and time key that a relation sets in a CREATE body is lifted into its own column and checked by the CREATE temporal key rule like the column, and null there for a key column answers 400. Generated BODY DTOs publish a relation over one column with its referenced column's described type and format (not a UUID for a varchar key; left out when bound or a PATCH/PUT key) and a relation over several join columns as an object of the join columns the client may write (bound columns, and on PATCH/PUT key columns, left out; none left drops the relation), each typed from the related entity's described column; an undescribed referenced column fails at startup. A request[PARAMETERS] transformer on a bound field overrides the path value. Row/query predicates are AND-merged with the binding and then HOOKS/IAM scope without overwrite semantics.
  • A rowPath that leaves key columns out reads the key first, take: 2 under the current scope and transaction. More than one match answers 500, counting rows whose left-out instants differ only below a millisecond as two; CREATE does not read the path and reloads by every key column TypeORM saved (a date and time column matches its whole millisecond, more than one match answers 500 and rolls back); a write answers 500 before any change when a left-out date and time column holds a finer value than a millisecond. Direct service.update/delete/get calls apply neither setting and need the complete key.
  • GET_LIST order config may declare server-only ordered defaultOrder and tieBreakers entries. These validate against all described direct scalar fields, including UUID columns left out of the client order fields; UUID and timestamptz columns listed in fields are client-sortable. A client order pair replaces defaults, tie-breakers are appended, and duplicate fields keep the earlier entry.
  • GET_LIST pagination defaults to PAGE. Explicit PAGE keeps required limit/page and the count envelope. CURSOR keeps required limit, accepts at most one of after/before, omits page, and returns only { items, nextCursor, previousCursor }.
  • CURSOR is a generated-route mode over the existing service: the first window uses one getMany query with take = limit + 1; a cursor window adds exactly one opposite-direction take = 1 probe. It does not add a provider, read model, service, cursor table, or signing-key configuration.
  • A CURSOR order must be explicit. Every possible order field must be a selected, persisted, described, non-null direct scalar with no TypeORM value transformer or accessor and must be unconditionally raw-exposed in the generated response; the complete primary key, single or composite, must form the final explicit tie-breakers, with each component exactly once, in any order among them, after every other order entry and absent from the client order allowlist.
  • CURSOR v1 is PostgreSQL-only and requires the standard text result parsers. Its exact TypeORM order-declaration matrix is: boolean; signed smallint and integer, including increment-generated columns physically emitted through SMALLSERIAL/SERIAL DDL; numeric enums backed by smallint or integer; signed bigint, including increment-generated BIGSERIAL DDL, exposed as canonical decimal BIGINT_STRING; native uuid; and timestamptz with a declared integer precision from 0 to 3, described as DATE_TIME so a JavaScript Date round-trips exactly. An unset precision, any other precision, timestamp, and any date column the active driver does not store as timestamptz, such as an untyped @CreateDateColumn, fail at bootstrap; do not add microsecond or raw-text paths. Temporal token values are canonical ISO-8601 UTC strings with millisecond precision for years 0001 through 9999. Do not use `smalls

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
279
Repository forks
1
7-day trend score
28

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Extension points

  • hookPre/post lifecycle hooks

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/elsikora/nestjs-crud-automator/crud-automator/badge)](https://verified-skill.com/skills/elsikora/nestjs-crud-automator/crud-automator)

Something wrong with this skill or its page? Tell us.