elsikora/nestjs-crud-automator/crud-automator
Claude Code and agent skill by elsikora/nestjs-crud-automator
- community
- ext:[{"type":"hook","description":"Pre/post lifecycle hooks"}]
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-automatorSemi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard
SKILL.md
Crud Automator
Source Priority
Use local source as the contract:
src/interface/**,src/type/**, and exported barrels define public API shape.test/unit/**andtest/e2e/**show supported behavior.docs/**andREADME.mdexplain behavior, but may drift and must be checked against source.
Default Workflow
- Inspect the existing entity, service, controller, subscriber, policy, and DTO configuration.
- Prefer native Crud Automator primitives before adding custom controllers, mappers, facades, or wrappers.
- Verify route config against current interfaces before copying examples.
- Validate Swagger, DTO shape, relation loading, subscriber firing, authorization decisions, and transaction behavior after meaningful changes.
Current Contract Reminders
@ApiController()applies Nest@Controller()internally; usepath.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), andDELETE. GET_MANYis service/function/subscriber-only, not a generated HTTP route.- Route controls live under
generation:generation.isEnabled,generation.shouldWriteToController, andgeneration.decorators. - Route security lives under
security.authenticationandsecurity.authorization. authentication.typeis 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, andQUERY. - Response config is target keyed with
EApiControllerResponseTarget.RESPONSE. - OpenAPI response headers live directly under
response.headers, not underEApiControllerResponseTarget.RESPONSE. - Generated routes declare exactly the error statuses they can return:
400and500always,404on row routes and on CREATE withrelations.request(a securable CREATE outside the policy scope answers403, which the securable controller declares),409on CREATE and updates,413and415on every route (Express parses a GET body, Fastify a DELETE body),401and403with any guard (security.authentication,execution.guards,@UseGuardson the class or the route, also on a REQUEST-scoped subclass without its own@ApiController, notAPP_GUARD),403with a securable controller withauthorization, plus each validator exception status, also oneHttpStatusdoes not list. GET_LIST never declares404. Custom routes (@ApiRouteCustom,@ApiMethod) get401/403by the same rule,429withthrottling.default,400/413/415/500on every route, each validator exception status, and404with an enabled relation inrelations.request.load.includeor any key inrelations.response.load.include, also one set tofalse; what a handler throws comes fromresponse.errors. Application-owned statuses such as a globalThrottlerGuard429go indocumentation.response.statuses, which replaces the default response of that status. Every status requires onlystatusCodeandmessage. autoDtois validators-only. EntityApiPropertyDescribe({ properties })remains the capability baseline; generated GET_LISTrequest[QUERY].filter,order, andpaginationown 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 ortruepreserves normal generation.- Route/DTO
isEnabled: true, generated GET identity,pathScope,rowPath, and query plans cannot re-enable a globally hidden property. ExplicitApiPropertyCopymay 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
dtoandautoDtoroute 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-levelpathScope. - 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, orpathScope-bound columns fail at startup. A date and time key column in the path of a composite key must betimestamptzwith precision 0 to 3, ordatetimewith 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-levelreadkey. pathScopeandrowPathare mutually exclusive with a manual PARAMETERS DTO on a route. They create route-scoped<Prefix><Route>ParametersDTOcomponents. Bound fields leave generated bodies; CREATE takes the value from the path and answers400for a body naming another one, relation keys included; writes outside the binding answer404. 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 answers400, 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 answers400), and PATCH/PUT keep key columns. A relation over one column is its referenced column's value (aDatefrom 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 answers400;nullfor a key column answers400. 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 answers400; clear it with a non-key own column such asparentId: 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, andnullthere for a key column answers400. Generated BODY DTOs publish a relation over one column with its referenced column's described type and format (not a UUID for avarcharkey; 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. Arequest[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
rowPaththat leaves key columns out reads the key first,take: 2under the current scope and transaction. More than one match answers500, 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 answers500and rolls back); a write answers500before any change when a left-out date and time column holds a finer value than a millisecond. Directservice.update/delete/getcalls apply neither setting and need the complete key. - GET_LIST order config may declare server-only ordered
defaultOrderandtieBreakersentries. These validate against all described direct scalar fields, including UUID columns left out of the client orderfields; UUID andtimestamptzcolumns listed infieldsare 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. ExplicitPAGEkeeps requiredlimit/pageand the count envelope.CURSORkeeps requiredlimit, accepts at most one ofafter/before, omitspage, and returns only{ items, nextCursor, previousCursor }. CURSORis a generated-route mode over the existing service: the first window uses onegetManyquery withtake = limit + 1; a cursor window adds exactly one opposite-directiontake = 1probe. It does not add a provider, read model, service, cursor table, or signing-key configuration.- A
CURSORorder 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; signedsmallintandinteger, including increment-generated columns physically emitted throughSMALLSERIAL/SERIALDDL; numeric enums backed bysmallintorinteger; signedbigint, including increment-generatedBIGSERIALDDL, exposed as canonical decimalBIGINT_STRING; nativeuuid; andtimestamptzwith a declared integerprecisionfrom 0 to 3, described asDATE_TIMEso a JavaScriptDateround-trips exactly. An unset precision, any other precision,timestamp, and any date column the active driver does not store astimestamptz, 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
- SKILL.md in the repositoryCheckingai/crud-automator/SKILL.md
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
[](https://verified-skill.com/skills/elsikora/nestjs-crud-automator/crud-automator)