cabloy 5.1.49 → 5.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/cabloy-backend-scaffold/SKILL.md +207 -0
- package/.claude/skills/cabloy-backend-scaffold/evals/evals.json +29 -0
- package/.claude/skills/cabloy-backend-scaffold/references/backend-thread-map.md +52 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +39 -0
- package/.claude/skills/cabloy-contract-loop/SKILL.md +201 -0
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +29 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +47 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +32 -0
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +230 -0
- package/.claude/skills/cabloy-frontend-scaffold/evals/evals.json +35 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +41 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/frontend-thread-map.md +54 -0
- package/.claude/skills/cabloy-workflow/SKILL.md +288 -0
- package/.claude/skills/cabloy-workflow/evals/evals.json +35 -0
- package/.claude/skills/cabloy-workflow/references/cli-strategy.md +39 -0
- package/.claude/skills/cabloy-workflow/references/edition-detection.md +29 -0
- package/.github/workflows/docs-pages.yml +54 -0
- package/.gitignore +1 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +59 -0
- package/README.md +137 -0
- package/cabloy-docs/.vitepress/config.mjs +222 -0
- package/cabloy-docs/.vitepress/public/CNAME +1 -0
- package/cabloy-docs/.vitepress/theme/custom.css +64 -0
- package/cabloy-docs/.vitepress/theme/edition-badges.md +5 -0
- package/cabloy-docs/.vitepress/theme/index.js +5 -0
- package/cabloy-docs/ai/class-placement-rule.md +138 -0
- package/cabloy-docs/ai/cli-for-agents.md +56 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +165 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +167 -0
- package/cabloy-docs/ai/edition-detection.md +30 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +135 -0
- package/cabloy-docs/ai/global-bean-lookup.md +157 -0
- package/cabloy-docs/ai/introduction.md +62 -0
- package/cabloy-docs/ai/playbook-backend-module.md +111 -0
- package/cabloy-docs/ai/playbook-contract-regeneration.md +100 -0
- package/cabloy-docs/ai/playbook-frontend-page.md +99 -0
- package/cabloy-docs/ai/playbook-metadata-refresh.md +67 -0
- package/cabloy-docs/ai/repo-guidance.md +58 -0
- package/cabloy-docs/ai/rules-and-config.md +29 -0
- package/cabloy-docs/ai/skills.md +35 -0
- package/cabloy-docs/ai/verification.md +30 -0
- package/cabloy-docs/backend/aop-overview.md +128 -0
- package/cabloy-docs/backend/auth-guide.md +151 -0
- package/cabloy-docs/backend/backend-essentials.md +128 -0
- package/cabloy-docs/backend/broadcast-guide.md +138 -0
- package/cabloy-docs/backend/cache-guide.md +70 -0
- package/cabloy-docs/backend/captcha-guide.md +162 -0
- package/cabloy-docs/backend/cli.md +173 -0
- package/cabloy-docs/backend/config-guide.md +249 -0
- package/cabloy-docs/backend/controller-aop-guide.md +270 -0
- package/cabloy-docs/backend/controller-guide.md +347 -0
- package/cabloy-docs/backend/crud-workflow.md +118 -0
- package/cabloy-docs/backend/dto-guide.md +161 -0
- package/cabloy-docs/backend/dto-infer-generation.md +153 -0
- package/cabloy-docs/backend/dynamic-datasource-guide.md +70 -0
- package/cabloy-docs/backend/election-guide.md +141 -0
- package/cabloy-docs/backend/entity-guide.md +150 -0
- package/cabloy-docs/backend/error-guide.md +108 -0
- package/cabloy-docs/backend/event-guide.md +183 -0
- package/cabloy-docs/backend/external-aop-guide.md +149 -0
- package/cabloy-docs/backend/field-indexes.md +79 -0
- package/cabloy-docs/backend/foundation.md +281 -0
- package/cabloy-docs/backend/i18n-guide.md +211 -0
- package/cabloy-docs/backend/internal-aop-guide.md +181 -0
- package/cabloy-docs/backend/introduction.md +95 -0
- package/cabloy-docs/backend/jwt-guide.md +276 -0
- package/cabloy-docs/backend/logger-guide.md +223 -0
- package/cabloy-docs/backend/mail-guide.md +189 -0
- package/cabloy-docs/backend/menu-guide.md +80 -0
- package/cabloy-docs/backend/migration-and-changes.md +192 -0
- package/cabloy-docs/backend/model-guide.md +274 -0
- package/cabloy-docs/backend/multi-database-datasource.md +171 -0
- package/cabloy-docs/backend/multi-instance-and-instance-resolution.md +196 -0
- package/cabloy-docs/backend/openapi-guide.md +118 -0
- package/cabloy-docs/backend/orm-aggregate-group-guide.md +210 -0
- package/cabloy-docs/backend/orm-configuration-guide.md +165 -0
- package/cabloy-docs/backend/orm-guide.md +109 -0
- package/cabloy-docs/backend/orm-mutation-guide.md +195 -0
- package/cabloy-docs/backend/orm-select-guide.md +243 -0
- package/cabloy-docs/backend/queue-guide.md +271 -0
- package/cabloy-docs/backend/quickstart.md +141 -0
- package/cabloy-docs/backend/redis-guide.md +70 -0
- package/cabloy-docs/backend/redlock-guide.md +60 -0
- package/cabloy-docs/backend/relations-guide.md +250 -0
- package/cabloy-docs/backend/runtime-and-flavors.md +304 -0
- package/cabloy-docs/backend/schedule-guide.md +81 -0
- package/cabloy-docs/backend/scripts.md +116 -0
- package/cabloy-docs/backend/serialization-guide.md +192 -0
- package/cabloy-docs/backend/service-guide.md +166 -0
- package/cabloy-docs/backend/sharding-guide.md +49 -0
- package/cabloy-docs/backend/startup-guide.md +326 -0
- package/cabloy-docs/backend/transaction-guide.md +82 -0
- package/cabloy-docs/backend/unit-testing.md +209 -0
- package/cabloy-docs/backend/upload-guide.md +160 -0
- package/cabloy-docs/backend/user-access-guide.md +157 -0
- package/cabloy-docs/backend/validation-guide.md +80 -0
- package/cabloy-docs/backend/worker-guide.md +59 -0
- package/cabloy-docs/editions/cabloy-basic.md +25 -0
- package/cabloy-docs/editions/cabloy-start.md +24 -0
- package/cabloy-docs/editions/detection.md +31 -0
- package/cabloy-docs/editions/overview.md +44 -0
- package/cabloy-docs/frontend/api-guide.md +93 -0
- package/cabloy-docs/frontend/api-schema-guide.md +43 -0
- package/cabloy-docs/frontend/app-startup-guide.md +185 -0
- package/cabloy-docs/frontend/cli.md +78 -0
- package/cabloy-docs/frontend/component-guide.md +105 -0
- package/cabloy-docs/frontend/component-props-guide.md +97 -0
- package/cabloy-docs/frontend/component-v-model-guide.md +110 -0
- package/cabloy-docs/frontend/css-in-js-guide.md +151 -0
- package/cabloy-docs/frontend/design-principles.md +55 -0
- package/cabloy-docs/frontend/environment-config-guide.md +250 -0
- package/cabloy-docs/frontend/foundation.md +57 -0
- package/cabloy-docs/frontend/generic-component-guide.md +35 -0
- package/cabloy-docs/frontend/icon-engine-guide.md +88 -0
- package/cabloy-docs/frontend/introduction.md +32 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +211 -0
- package/cabloy-docs/frontend/mock-guide.md +109 -0
- package/cabloy-docs/frontend/model-architecture.md +87 -0
- package/cabloy-docs/frontend/model-state-guide.md +70 -0
- package/cabloy-docs/frontend/module-scope.md +168 -0
- package/cabloy-docs/frontend/modules-and-suites.md +179 -0
- package/cabloy-docs/frontend/navigation-guards-guide.md +68 -0
- package/cabloy-docs/frontend/openapi-sdk-guide.md +102 -0
- package/cabloy-docs/frontend/page-guide.md +223 -0
- package/cabloy-docs/frontend/page-params-guide.md +87 -0
- package/cabloy-docs/frontend/page-query-guide.md +96 -0
- package/cabloy-docs/frontend/page-route-guide.md +147 -0
- package/cabloy-docs/frontend/quickstart.md +201 -0
- package/cabloy-docs/frontend/route-alias-guide.md +61 -0
- package/cabloy-docs/frontend/scripts.md +86 -0
- package/cabloy-docs/frontend/sdk-guide.md +45 -0
- package/cabloy-docs/frontend/server-data.md +74 -0
- package/cabloy-docs/frontend/ssr-client-only.md +40 -0
- package/cabloy-docs/frontend/ssr-env.md +51 -0
- package/cabloy-docs/frontend/ssr-init-data.md +46 -0
- package/cabloy-docs/frontend/ssr-overview.md +48 -0
- package/cabloy-docs/frontend/ssr-seo-meta.md +52 -0
- package/cabloy-docs/frontend/system-startup-guide.md +186 -0
- package/cabloy-docs/frontend/theme-guide.md +154 -0
- package/cabloy-docs/frontend/zod-guide.md +161 -0
- package/cabloy-docs/fullstack/edition-collaboration-differences.md +61 -0
- package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +64 -0
- package/cabloy-docs/fullstack/introduction.md +69 -0
- package/cabloy-docs/fullstack/openapi-to-sdk.md +116 -0
- package/cabloy-docs/fullstack/quickstart.md +86 -0
- package/cabloy-docs/fullstack/vona-zova-integration.md +86 -0
- package/cabloy-docs/index.md +73 -0
- package/cabloy-docs/package.json +16 -0
- package/cabloy-docs/pnpm-lock.yaml +1607 -0
- package/cabloy-docs/reference/backend-directory-structure.md +88 -0
- package/cabloy-docs/reference/cli-reference.md +49 -0
- package/cabloy-docs/reference/glossary.md +38 -0
- package/cabloy-docs/reference/package-map.md +105 -0
- package/cabloy-docs/reference/repo-scripts.md +36 -0
- package/package.json +4 -1
- package/scripts/init.ts +12 -0
- package/scripts/upgrade.ts +31 -3
- package/vona/README.md +3 -3
- package/vona/README.zh-CN.md +4 -4
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-datasharding/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-datasharding/src/bean/summerCache.datasourceWrite.ts +2 -2
- package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
- package/vona/src/suite-vendor/a-captcha/modules/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-captcha/modules/a-captcha/src/bean/cacheRedis.captcha.ts +2 -2
- package/vona/src/suite-vendor/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-bean/cli/bean/metadata/generate.ts +5 -6
- package/vona/src/suite-vendor/a-vona/modules/a-bean/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-cache/cli/cacheMem/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/cli/cacheRedis/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/.metadata/index.ts +13 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/bean/bean.cache.ts +6 -7
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/{bean/bean.cacheMemBase.ts → service/cacheMemBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/{bean/bean.cacheRedisBase.ts → service/cacheRedisBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/src/bean/cacheRedis.emailConfirm.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/src/bean/cacheRedis.passwordReset.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/src/bean/summerCache.json.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/cli/databaseDialect/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/.metadata/index.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/bean.database.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/bean.model.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/schedule.softDeletionPrune.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/common/utils.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/index.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_cache.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_meta.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.modelBase.ts +0 -5
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAggregate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoCreate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGet.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGroup.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoSelectAndCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoUpdate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/modelCacheBase.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relations.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsDynamic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsStatic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/cacheEntity_.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/cacheQuery_.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/database.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean/bean.databaseDialectBase.ts → service/databaseDialectBase_.ts} +38 -41
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/db_.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/relations_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/database.ts +0 -5
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoGet.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoSelectAndCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/model.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelAggr.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelGeneral.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelGroup.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelIncrement.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relations.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsColumns.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDef.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDefDynamic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDefMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsTables.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.betterSqlite3.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.mysql.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.pg.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +109 -40
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/bean/cacheRedis.startupDebounce.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-summer/cli/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-summer/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/.metadata/index.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/bean/bean.summer.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/service/localMem_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/service/localRedis_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/{bean/bean.summerCacheBase.ts → service/summerCacheBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/src/bean/summerCache.rapidoc.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/src/bean/summerCache.swagger.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/cacheRedis.authToken.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-worker/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-worker/src/bean/cacheRedis.workerAlive.ts +2 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/README.md +4 -4
- package/zova/README.zh-CN.md +4 -4
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolTwo/controller.tsx +0 -2
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/a-home/modules/home-base/src/page/errorNotFound/controller.tsx +8 -2
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +1 -1
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.header.tsx +1 -1
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.locale.tsx +2 -5
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.tabs.tsx +1 -4
- package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/bean/sys.router.ts +28 -6
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkeySys.ts +15 -3
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/router.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +4 -0
- package/zova/src/suite-vendor/a-zova/package.json +3 -3
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud_inner.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud_table.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_knex.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_utils.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_view.ts +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Controller AOP Guide
|
|
2
|
+
|
|
3
|
+
## Why controller AOP matters
|
|
4
|
+
|
|
5
|
+
Controller AOP is how Vona structures request-path behavior around controller actions.
|
|
6
|
+
|
|
7
|
+
Instead of scattering authentication, validation, logging, error handling, or request transformation into unrelated code paths, Vona models them as explicit aspect families.
|
|
8
|
+
|
|
9
|
+
## The five controller aspect families
|
|
10
|
+
|
|
11
|
+
Vona provides five main controller-facing aspect families:
|
|
12
|
+
|
|
13
|
+
- **middleware**
|
|
14
|
+
- **guard**
|
|
15
|
+
- **interceptor**
|
|
16
|
+
- **pipe**
|
|
17
|
+
- **filter**
|
|
18
|
+
|
|
19
|
+
## Middleware
|
|
20
|
+
|
|
21
|
+
Middleware is used for request-path behavior that wraps execution before and after the controller action.
|
|
22
|
+
|
|
23
|
+
Representative CLI generation patterns from the legacy workflow include:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run vona :create:bean middlewareSystem logger -- --module=demo-student
|
|
27
|
+
npm run vona :create:bean middleware logger -- --module=demo-student --boilerplate=global
|
|
28
|
+
npm run vona :create:bean middleware logger -- --module=demo-student
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
These commands all go through the shared `:create:bean` entrypoint, but they target different middleware scopes and boilerplates.
|
|
32
|
+
|
|
33
|
+
### Scope variants
|
|
34
|
+
|
|
35
|
+
- **system middleware** runs before route matching
|
|
36
|
+
- **global middleware** is auto-loaded and applied broadly
|
|
37
|
+
- **local middleware** is attached directly to a controller class or action
|
|
38
|
+
|
|
39
|
+
### Why system middleware is different
|
|
40
|
+
|
|
41
|
+
System middleware executes before route matching, so it is the earliest controller-facing interception stage.
|
|
42
|
+
|
|
43
|
+
This is where Vona places concerns such as not-found handling, request override behavior, app initialization, instance initialization, HTTP logging, CORS, and static-resource handling.
|
|
44
|
+
|
|
45
|
+
### Representative local usage
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
@Aspect.middleware('demo-student:logger')
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Representative global usage
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
@Aspect.middlewareGlobal('demo-student:logger', { prefix: 'elapsed' })
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Representative built-in usage
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
@Core.gate({
|
|
61
|
+
gate: {
|
|
62
|
+
flavor: 'normal',
|
|
63
|
+
mode: 'dev',
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `@Core.gate(...)` shorthand still maps to `@Aspect.middlewareGlobal('a-core:gate', ...)`.
|
|
69
|
+
|
|
70
|
+
## Guard
|
|
71
|
+
|
|
72
|
+
Guards are used for access control and execution preconditions.
|
|
73
|
+
|
|
74
|
+
Typical jobs include:
|
|
75
|
+
|
|
76
|
+
- checking whether the current user is authenticated
|
|
77
|
+
- checking whether a user is activated
|
|
78
|
+
- checking whether a username or role name matches the required rule
|
|
79
|
+
|
|
80
|
+
### Scope variants
|
|
81
|
+
|
|
82
|
+
- **global guard** is auto-loaded and can be configured per API
|
|
83
|
+
- **local guard** is attached directly to a controller class or action
|
|
84
|
+
|
|
85
|
+
### Representative local usage
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
@Aspect.guard('demo-student:admin')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Representative built-in usage
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
@Passport.public()
|
|
95
|
+
@Passport.activated(false)
|
|
96
|
+
@Passport.userName({ name: 'admin' })
|
|
97
|
+
@Passport.roleName({ name: 'admin' })
|
|
98
|
+
@Passport.admin()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
These shorthands still map back to the generic aspect model.
|
|
102
|
+
|
|
103
|
+
For the underlying auth, passport, and user-access model, see [Auth Guide](/backend/auth-guide) and [User Access Guide](/backend/user-access-guide).
|
|
104
|
+
|
|
105
|
+
## Interceptor
|
|
106
|
+
|
|
107
|
+
Interceptors provide onion-style around-execution behavior for controller actions.
|
|
108
|
+
|
|
109
|
+
Typical jobs include:
|
|
110
|
+
|
|
111
|
+
- timing and logging
|
|
112
|
+
- wrapping response handling
|
|
113
|
+
- enforcing consistent around-action behavior
|
|
114
|
+
|
|
115
|
+
### Scope variants
|
|
116
|
+
|
|
117
|
+
- **global interceptor** is auto-loaded and broadly configurable
|
|
118
|
+
- **local interceptor** is attached directly to a controller class or action
|
|
119
|
+
|
|
120
|
+
### Representative local usage
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
@Aspect.interceptor('demo-student:logger')
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Representative built-in usage
|
|
127
|
+
|
|
128
|
+
Built-in interceptors can be used for framework-level response behavior, such as body-wrapping interceptors provided by built-in modules. Vona also provides interceptor-driven verification helpers such as `@Core.captchaVerify(...)`; for the provider/scene architecture behind that flow, see [Captcha Guide](/backend/captcha-guide).
|
|
129
|
+
|
|
130
|
+
## Pipe
|
|
131
|
+
|
|
132
|
+
Pipes transform or validate request values before they reach controller logic.
|
|
133
|
+
|
|
134
|
+
### Scope variants
|
|
135
|
+
|
|
136
|
+
- **global pipe** is auto-loaded and broadly configurable
|
|
137
|
+
- **local pipe** is attached directly to a controller class or action
|
|
138
|
+
- **argument pipe** is the most common developer-facing style in normal application code
|
|
139
|
+
|
|
140
|
+
### Representative local pipe usage
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
@Aspect.pipe('demo-student:number')
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Argument pipe pattern
|
|
147
|
+
|
|
148
|
+
Argument pipes are usually created from a local pipe:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { createArgumentPipe } from 'vona-module-a-aspect';
|
|
152
|
+
|
|
153
|
+
export const ArgNumber = createArgumentPipe('demo-student:number');
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Used on a controller parameter:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
async findOne(@ArgNumber() @Arg.param('id') id: any) {}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Order matters:
|
|
163
|
+
|
|
164
|
+
- `@Arg.param(...)`
|
|
165
|
+
- then custom argument pipes such as `@ArgNumber()`
|
|
166
|
+
|
|
167
|
+
### Zod integration
|
|
168
|
+
|
|
169
|
+
In many real cases, built-in `@Arg.*` handling with type inference or explicit Zod schema is enough:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
async findOne(@Arg.param('id') id: number) {}
|
|
173
|
+
async findOne(@Arg.param('id', z.number().min(1)) id: number) {}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This is why custom argument pipes are now the exception rather than the default. Reach for a custom argument pipe when the transformation itself is reusable business behavior. Reach for typed `@Arg.*` plus Zod when ordinary parameter coercion and validation are enough.
|
|
177
|
+
|
|
178
|
+
For broader validation guidance, see [Validation Guide](/backend/validation-guide).
|
|
179
|
+
|
|
180
|
+
## Filter
|
|
181
|
+
|
|
182
|
+
Filters handle exceptions and logging behavior.
|
|
183
|
+
|
|
184
|
+
This is where request-path error customization becomes explicit.
|
|
185
|
+
|
|
186
|
+
### Scope variants
|
|
187
|
+
|
|
188
|
+
- **global filter** is auto-loaded and can be tuned per API or by app config
|
|
189
|
+
- **local filter** is attached directly to a controller class or action
|
|
190
|
+
|
|
191
|
+
### Representative local usage
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
@Aspect.filter('demo-student:test')
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Built-in filter
|
|
198
|
+
|
|
199
|
+
The built-in global filter `a-error:error` covers common error-handling and logging needs.
|
|
200
|
+
|
|
201
|
+
Representative shorthand:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
@Core.error({ logs: { 422: false } })
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Shared configuration patterns
|
|
208
|
+
|
|
209
|
+
Most controller aspect families support the same configuration ideas:
|
|
210
|
+
|
|
211
|
+
- parameters with default values
|
|
212
|
+
- parameter override at usage site
|
|
213
|
+
- app-config override
|
|
214
|
+
- enable/disable
|
|
215
|
+
- `match` and `ignore`
|
|
216
|
+
- `mode` and `flavor`
|
|
217
|
+
- ordering through `dependencies` and `dependents`
|
|
218
|
+
- inspection of the effective aspect list
|
|
219
|
+
|
|
220
|
+
That consistency is one of the most important reasons controller AOP stays scalable in Vona.
|
|
221
|
+
|
|
222
|
+
### Representative precedence model
|
|
223
|
+
|
|
224
|
+
A representative precedence pattern is:
|
|
225
|
+
|
|
226
|
+
- usage-site override
|
|
227
|
+
- then app-config override in `config.onions`
|
|
228
|
+
- then decorator default values
|
|
229
|
+
|
|
230
|
+
For example, global middleware can define defaults in the bean, be overridden in app config, and then be overridden again at a specific controller action.
|
|
231
|
+
|
|
232
|
+
### Representative inspect patterns
|
|
233
|
+
|
|
234
|
+
Runtime inspection is especially useful when several global aspects combine on one route.
|
|
235
|
+
|
|
236
|
+
Representative examples include:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
this.bean.onion.middlewareSystem.inspect();
|
|
240
|
+
this.bean.onion.middleware.inspect();
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Those inspection helpers help explain why a route is behaving a certain way before you start rewriting aspect definitions.
|
|
244
|
+
|
|
245
|
+
## How to choose the right aspect family
|
|
246
|
+
|
|
247
|
+
Use this rule of thumb:
|
|
248
|
+
|
|
249
|
+
- **middleware** for request-path infrastructure behavior
|
|
250
|
+
- **guard** for access checks and authorization preconditions
|
|
251
|
+
- **interceptor** for around-execution behavior
|
|
252
|
+
- **pipe** for request-value transformation or validation
|
|
253
|
+
- **filter** for error handling and log customization
|
|
254
|
+
|
|
255
|
+
## Relationship to controller design
|
|
256
|
+
|
|
257
|
+
Controller AOP should be read together with [Controller Guide](/backend/controller-guide).
|
|
258
|
+
|
|
259
|
+
The controller guide explains routing, `@Web.*`, and `@Arg.*`, while this guide explains the cross-cutting behavior that surrounds controller execution.
|
|
260
|
+
|
|
261
|
+
## Questions for controller-AOP-sensitive changes
|
|
262
|
+
|
|
263
|
+
When changing controller behavior, ask:
|
|
264
|
+
|
|
265
|
+
1. is this concern really controller AOP rather than ordinary business logic?
|
|
266
|
+
2. should it be local, global, or system-level?
|
|
267
|
+
3. is there already a built-in shorthand or built-in aspect for this job?
|
|
268
|
+
4. does the change also affect validation, OpenAPI, runtime flavor, or environment-specific behavior?
|
|
269
|
+
|
|
270
|
+
That helps AI keep request-path logic aligned with Vona’s native execution model.
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Controller Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how controllers work in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
## Why controllers matter
|
|
6
|
+
|
|
7
|
+
Controllers are the HTTP-facing contract surface of the backend.
|
|
8
|
+
|
|
9
|
+
They define how routes, request parameters, validation, response typing, and OpenAPI metadata meet the rest of the backend thread.
|
|
10
|
+
|
|
11
|
+
A useful contract-loop mental model is:
|
|
12
|
+
|
|
13
|
+
- controller defines the route and request/response surface
|
|
14
|
+
- service owns orchestration
|
|
15
|
+
- model and entity shape persistence behavior
|
|
16
|
+
- DTOs and validation shape explicit request/response contracts
|
|
17
|
+
- OpenAPI emits the machine-readable contract that frontend SDK generation can consume
|
|
18
|
+
|
|
19
|
+
## Create a controller
|
|
20
|
+
|
|
21
|
+
Example: create a controller named `student` in module `demo-student`.
|
|
22
|
+
|
|
23
|
+
### CLI command
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run vona :create:bean controller student -- --module=demo-student
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Controller definition
|
|
30
|
+
|
|
31
|
+
Representative pattern:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
@Controller<IControllerOptionsStudent>('student')
|
|
35
|
+
export class ControllerStudent extends BeanBase {
|
|
36
|
+
@Web.post('')
|
|
37
|
+
@Api.body(v.tableIdentity())
|
|
38
|
+
async create(@Arg.body() student: DtoStudentCreate): Promise<TableIdentity> {
|
|
39
|
+
return (await this.scope.service.student.create(student)).id;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The most important things to notice are:
|
|
45
|
+
|
|
46
|
+
- the controller path is declared in `@Controller(...)`
|
|
47
|
+
- the action path and request method are declared with `@Web.*`
|
|
48
|
+
- request parameters are declared with `@Arg.*`
|
|
49
|
+
- response metadata can be enriched for validation and OpenAPI generation
|
|
50
|
+
|
|
51
|
+
## Route composition
|
|
52
|
+
|
|
53
|
+
Vona uses automatic route registration for controller actions that use `@Web` decorators.
|
|
54
|
+
|
|
55
|
+
The general route model is:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
Route Path = GlobalPrefix + Module Url + Controller Path + Action Path
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Important pieces:
|
|
62
|
+
|
|
63
|
+
- `GlobalPrefix`: from project config, commonly `/api`
|
|
64
|
+
- `Module Url`: derived from the module name
|
|
65
|
+
- controller path and action path: defined in the controller itself
|
|
66
|
+
|
|
67
|
+
## Route simplification rules
|
|
68
|
+
|
|
69
|
+
Three useful simplification rules apply here.
|
|
70
|
+
|
|
71
|
+
### 1. Remove duplicate module path fragments
|
|
72
|
+
|
|
73
|
+
If the controller path matches the module name, the duplicate fragment is removed from the final route.
|
|
74
|
+
|
|
75
|
+
### 2. Leading `/` removes the module URL
|
|
76
|
+
|
|
77
|
+
If the controller path or action path starts with `/`, the module URL is removed.
|
|
78
|
+
|
|
79
|
+
### 3. Leading `//` removes both global prefix and module URL
|
|
80
|
+
|
|
81
|
+
If the controller path or action path starts with `//`, both the global prefix and module URL are removed.
|
|
82
|
+
|
|
83
|
+
This is useful for special routes such as the project homepage or a shared non-module-prefixed API path.
|
|
84
|
+
|
|
85
|
+
## Request methods
|
|
86
|
+
|
|
87
|
+
Vona groups HTTP method decorators under `@Web`, which helps reduce mental overhead.
|
|
88
|
+
|
|
89
|
+
Representative methods include:
|
|
90
|
+
|
|
91
|
+
- `@Web.post`
|
|
92
|
+
- `@Web.get`
|
|
93
|
+
- `@Web.delete`
|
|
94
|
+
- `@Web.put`
|
|
95
|
+
- `@Web.patch`
|
|
96
|
+
- `@Web.options`
|
|
97
|
+
- `@Web.head`
|
|
98
|
+
|
|
99
|
+
## Request parameters
|
|
100
|
+
|
|
101
|
+
Vona groups request-parameter decorators under `@Arg`.
|
|
102
|
+
|
|
103
|
+
Representative parameter decorators include:
|
|
104
|
+
|
|
105
|
+
- `@Arg.param`
|
|
106
|
+
- `@Arg.query`
|
|
107
|
+
- `@Arg.body`
|
|
108
|
+
- `@Arg.headers`
|
|
109
|
+
- `@Arg.fields`
|
|
110
|
+
- `@Arg.field`
|
|
111
|
+
- `@Arg.files`
|
|
112
|
+
- `@Arg.file`
|
|
113
|
+
- `@Arg.user`
|
|
114
|
+
|
|
115
|
+
These decorators are also the main request-parameter surface for multipart upload flows; see [Upload Guide](/backend/upload-guide).
|
|
116
|
+
|
|
117
|
+
## Parameter extraction patterns
|
|
118
|
+
|
|
119
|
+
A useful distinction is:
|
|
120
|
+
|
|
121
|
+
- specify a field name when you want one parameter only
|
|
122
|
+
- omit the field name when you want the whole structured object
|
|
123
|
+
|
|
124
|
+
Representative patterns:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
findOne(@Arg.query('id') id: number) {}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
class DtoStudentInfo {
|
|
132
|
+
id: number;
|
|
133
|
+
name: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
findOne(@Arg.query() query: DtoStudentInfo) {
|
|
137
|
+
console.log(query.id, query.name);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
This matters because controller signatures can express both simple and structured request shapes without leaving the framework’s contract surface.
|
|
142
|
+
|
|
143
|
+
## Compact action-signature patterns
|
|
144
|
+
|
|
145
|
+
A practical controller signature often combines route params, request body typing, and response metadata in one place.
|
|
146
|
+
|
|
147
|
+
Representative pattern:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
@Web.patch('updateUser/:id')
|
|
151
|
+
updateUser(
|
|
152
|
+
@Arg.param('id') id: TableIdentity,
|
|
153
|
+
@Arg.body(v.object(DtoUserUpdate)) user: DtoUserUpdate,
|
|
154
|
+
) {
|
|
155
|
+
return this.scope.model.user.update(user, { where: { id } });
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
When the response contract should follow inferred DTO shape directly, a pattern like this is also common:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
@Web.get('getUserDynamic')
|
|
163
|
+
@Api.body($Dto.get('test-vona:post'))
|
|
164
|
+
getPostDynamic() {}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
A practical rule is:
|
|
168
|
+
|
|
169
|
+
- use `@Arg.param(...)` and `@Arg.body(...)` together when the action mixes route identity and structured payload input
|
|
170
|
+
- use return-type inference when the response contract is obvious
|
|
171
|
+
- use `@Api.body(...)` when the response should expose a more specific inferred or customized contract shape
|
|
172
|
+
|
|
173
|
+
## Validation, OpenAPI, and Controller AOP
|
|
174
|
+
|
|
175
|
+
Controllers are strongly connected to three related capabilities:
|
|
176
|
+
|
|
177
|
+
- parameter validation based on Zod-oriented patterns
|
|
178
|
+
- Swagger/OpenAPI generation
|
|
179
|
+
- middleware, guards, interceptors, pipes, and filters around the request path
|
|
180
|
+
|
|
181
|
+
In practice, that means controllers are not only request handlers. They are also a key place where request and response shape become machine-readable for tooling and frontend integration, and where request-path policies are composed through controller AOP.
|
|
182
|
+
|
|
183
|
+
For a dedicated explanation of middleware, guards, interceptors, pipes, and filters, see [Controller AOP Guide](/backend/controller-aop-guide).
|
|
184
|
+
|
|
185
|
+
## Response body typing and schema declaration
|
|
186
|
+
|
|
187
|
+
Vona can often infer response schema from the declared return type.
|
|
188
|
+
|
|
189
|
+
Representative automatically inferred cases include:
|
|
190
|
+
|
|
191
|
+
- basic types such as `string`, `number`, and `boolean`
|
|
192
|
+
- DTO classes
|
|
193
|
+
- Entity classes
|
|
194
|
+
|
|
195
|
+
When inference is not enough, use explicit schema declaration.
|
|
196
|
+
|
|
197
|
+
Representative pattern:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
@Api.body(v.array(String))
|
|
201
|
+
findOne(): string[] {
|
|
202
|
+
return ['Tom'];
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
A practical rule is:
|
|
207
|
+
|
|
208
|
+
- use return-type inference when the contract is obvious and simple
|
|
209
|
+
- use explicit `@Api.body(...)` when the response shape needs more control or the inference boundary becomes unclear
|
|
210
|
+
|
|
211
|
+
## Response wrapper behavior
|
|
212
|
+
|
|
213
|
+
By default, Vona wraps the response body in a standard wrapper object.
|
|
214
|
+
|
|
215
|
+
That means a plain response value conceptually becomes a response shape like:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
code: string;
|
|
220
|
+
message: string;
|
|
221
|
+
data: string;
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Disable the wrapper
|
|
226
|
+
|
|
227
|
+
Use `@Api.bodyCustom(false)` when the endpoint should return the body directly.
|
|
228
|
+
|
|
229
|
+
Representative pattern:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
@Api.bodyCustom(false)
|
|
233
|
+
findOne(): string {
|
|
234
|
+
return 'Tom';
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Provide a custom wrapper
|
|
239
|
+
|
|
240
|
+
Use `@Api.bodyCustom(...)` with a wrapper function when the project needs a different response envelope.
|
|
241
|
+
|
|
242
|
+
Representative pattern:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
@Api.bodyCustom(bodySchemaWrapperCustom)
|
|
246
|
+
findOne(): string {
|
|
247
|
+
return 'Tom';
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
This is one of the most important response-contract choices because it affects backend OpenAPI output and frontend SDK consumption.
|
|
252
|
+
|
|
253
|
+
Other response/action metadata patterns can also be expressed at the controller surface, including representative cases such as:
|
|
254
|
+
|
|
255
|
+
- `contentType`
|
|
256
|
+
- `httpCode`
|
|
257
|
+
- `headers`
|
|
258
|
+
- `setHeaders`
|
|
259
|
+
- `exclude`
|
|
260
|
+
- `tags`
|
|
261
|
+
|
|
262
|
+
Use those when the runtime behavior and the machine-readable contract should stay aligned in one place.
|
|
263
|
+
|
|
264
|
+
## Action options
|
|
265
|
+
|
|
266
|
+
Vona actions can carry additional metadata directly in `@Web.*` options.
|
|
267
|
+
|
|
268
|
+
Representative pattern:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
@Web.get(':id', {
|
|
272
|
+
tags: ['Student'],
|
|
273
|
+
description: 'Find a student',
|
|
274
|
+
})
|
|
275
|
+
findOne(@Arg.param('id') id: number): EntityStudent {}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Representative action-option areas include:
|
|
279
|
+
|
|
280
|
+
- `description`
|
|
281
|
+
- `summary`
|
|
282
|
+
- `httpCode`
|
|
283
|
+
- `contentType`
|
|
284
|
+
- `bodySchema`
|
|
285
|
+
- `bodySchemaWrapper`
|
|
286
|
+
- `exclude`
|
|
287
|
+
- `tags`
|
|
288
|
+
- `operationId`
|
|
289
|
+
- `headers`
|
|
290
|
+
- `setHeaders`
|
|
291
|
+
|
|
292
|
+
This matters because controller actions are one of the main places where runtime behavior and machine-readable API description meet.
|
|
293
|
+
|
|
294
|
+
## Controller options
|
|
295
|
+
|
|
296
|
+
Controllers can also carry higher-level options.
|
|
297
|
+
|
|
298
|
+
Representative pattern:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
@Controller('student', {
|
|
302
|
+
exclude: false,
|
|
303
|
+
tags: ['Student'],
|
|
304
|
+
})
|
|
305
|
+
class ControllerStudent {}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Representative controller-option areas include:
|
|
309
|
+
|
|
310
|
+
- `exclude`
|
|
311
|
+
- `tags`
|
|
312
|
+
- `actions`
|
|
313
|
+
- `enable`
|
|
314
|
+
- `meta`
|
|
315
|
+
|
|
316
|
+
This is especially important because the controller surface can also be tuned from app config through onion/config override patterns.
|
|
317
|
+
|
|
318
|
+
## Relationship to the backend contract loop
|
|
319
|
+
|
|
320
|
+
Read this guide together with:
|
|
321
|
+
|
|
322
|
+
- [Validation Guide](/backend/validation-guide)
|
|
323
|
+
- [DTO Guide](/backend/dto-guide)
|
|
324
|
+
- [OpenAPI Guide](/backend/openapi-guide)
|
|
325
|
+
- [CRUD Workflow](/backend/crud-workflow)
|
|
326
|
+
- [Unit Testing](/backend/unit-testing)
|
|
327
|
+
- [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
|
|
328
|
+
|
|
329
|
+
A practical split is:
|
|
330
|
+
|
|
331
|
+
- controllers define the route and contract surface
|
|
332
|
+
- validation and DTOs define the request/response schema language
|
|
333
|
+
- OpenAPI emits the machine-readable contract
|
|
334
|
+
- CRUD generation instantiates the thread quickly
|
|
335
|
+
- tests verify the contract through action execution
|
|
336
|
+
|
|
337
|
+
## Practical implications for controller implementation
|
|
338
|
+
|
|
339
|
+
When creating or editing a controller, preserve the Vona controller model instead of rewriting it into a generic framework style.
|
|
340
|
+
|
|
341
|
+
The safest workflow is:
|
|
342
|
+
|
|
343
|
+
1. use the Vona CLI to create the controller skeleton
|
|
344
|
+
2. inspect the generated module-specific patterns
|
|
345
|
+
3. add `@Web`, `@Arg`, validation, and OpenAPI metadata in the same style
|
|
346
|
+
4. choose deliberately whether response wrapper defaults should stay in place
|
|
347
|
+
5. verify the resulting routes and response conventions
|