{"id":27918367,"url":"https://github.com/koatty/koatty_doc","last_synced_at":"2026-02-18T19:31:10.386Z","repository":{"id":107608907,"uuid":"223318678","full_name":"Koatty/koatty_doc","owner":"Koatty","description":"Document for Koatty.","archived":false,"fork":false,"pushed_at":"2026-02-11T03:45:39.000Z","size":964,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-11T08:50:00.980Z","etag":null,"topics":["docmentation"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Koatty.png","metadata":{"files":{"readme":"docs/README-en.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-11-22T03:48:27.000Z","updated_at":"2026-02-11T03:45:42.000Z","dependencies_parsed_at":"2024-01-03T22:25:56.865Z","dependency_job_id":"eca976f0-f7f8-40cf-a6ad-67a761ab1db6","html_url":"https://github.com/Koatty/koatty_doc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Koatty/koatty_doc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koatty%2Fkoatty_doc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koatty%2Fkoatty_doc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koatty%2Fkoatty_doc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koatty%2Fkoatty_doc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Koatty","download_url":"https://codeload.github.com/Koatty/koatty_doc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koatty%2Fkoatty_doc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29591881,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T18:54:29.675Z","status":"ssl_error","status_checked_at":"2026-02-18T18:50:50.517Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docmentation"],"created_at":"2025-05-06T18:21:45.761Z","updated_at":"2026-02-18T19:31:10.357Z","avatar_url":"https://github.com/Koatty.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Koatty\n\nKoatty is an agile development framework based on Koa2, featuring automatic dependency injection (IoC) and Aspect-Oriented Programming (AOP) using TypeScript decorators, similar to SpringBoot.\n\n## Quick Start\n\n### First Application\n\n1. **Install the Command Line Tool**\n   \n   ```bash\n   npm i -g koatty_cli\n   ```\n   \n   The version of the command line tool corresponds to the version of the Koatty framework. For example, `koatty_cli@1.11.x` supports the new features of `koatty@1.11.x`.\n\n2. **Create a New Project**\n   \n   ```bash\n   kt new projectName\n   cd ./projectName\n   yarn install\n   ```\n\n3. **Start the Service**\n   \n   - **Development Mode**\n     \n     ```bash\n     npm run dev\n     ```\n   \n   - **Production Mode**\n     \n     ```bash\n     npm start\n     ```\n     \n     Access the application in your browser at `http://localhost:3000`.\n\n### Debugging Mode\n\nIt is highly recommended to use Visual Studio Code (VSCode) for development. Edit the `.vscode/launch.json` file in the project directory (you can also open it by clicking on Debug \u003e Add Configuration):\n\n```json\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"TS Program\",\n      \"args\": [\n        \"${workspaceRoot}/src/App.ts\"\n      ],\n      \"runtimeArgs\": [\n        \"--nolazy\",\n        \"-r\",\n        \"ts-node/register\"\n      ],\n      \"sourceMaps\": true,\n      \"cwd\": \"${workspaceRoot}\",\n      \"protocol\": \"inspector\",\n      \"internalConsoleOptions\": \"neverOpen\"\n    }\n  ]\n}\n```\n\nSelect `TS Program` to start in debug mode and access `http://127.0.0.1:3000`.\n\n### Unit Testing\n\nKoatty currently only supports Jest for writing test cases.\n\n```javascript\nimport request from 'supertest';\nimport { ExecBootStrap } from 'koatty';\nimport { App } from '../src/App';\n\ndescribe('UT example', () =\u003e {\n  let server;\n  beforeAll(async () =\u003e {\n    const appInstance = await ExecBootStrap()(App);\n    server = appInstance.callback();\n  });\n\n  it('request', async (done) =\u003e {\n    const rsp = await request(server).get('/path/to/server');\n    expect(rsp.status).toBe(200);\n    done();\n  });\n});\n```\n\n## Basic Features\n\n### Project Structure\n\nThe Koatty CLI `koatty_cli` creates the following directory structure by default when creating a project:\n\n```\n\u003cprojectName\u003e\n├── .vscode                       # VSCode configuration\n│   └── launch.json               # Node local debugging script\n├── dist                          # Compiled directory\n├── src                           # Project source code\n│   ├── config\n│   │   ├── config.ts             # Framework configuration\n│   │   ├── db.ts                 # Database configuration\n│   │   ├── middleware.ts         # Middleware configuration\n│   │   ├── plugin.ts             # Plugin configuration\n│   │   └── router.ts             # Router configuration\n│   ├── aspect                    # AOP aspect classes\n│   │   └── TestAspect.ts\n│   ├── controller                # Controllers\n│   │   └── TestController.ts\n│   ├── middleware                # Middleware\n│   │   ├── JwtMiddleware.ts\n│   │   └── ViewMiddleware.ts\n│   ├── model                     # Persistence layer\n│   │   └── TestModel.ts\n│   ├── plugin                    # Plugins\n│   │   └── TestPlugin.ts\n│   ├── proto                     # Protocol Buffers\n│   │   └── test.proto\n│   ├── resource                  # Static data or whitelist\n│   │   └── data.json\n│   ├── service                   # Service logic\n│   │   └── TestService.ts\n│   ├── utils                     # Utility functions\n│   │   └── tool.ts\n│   └── App.ts                    # Entry file\n├── static                        # Static files directory\n│   └── index.html\n├── test                          # Test cases\n│   └── index.test.js\n├── apidoc.json\n├── pm2.json\n├── package.json\n├── README.md\n└── tsconfig.json\n```\n\nHowever, Koatty supports flexible customization of the project structure. Except for the configuration directory (which can be customized through `@ConfigurationScan()`), and the static resources directory (which requires modifying the default configuration of the Static middleware), other directory names and structures can be customized.\n\n### Entry File\n\nThe default entry file for Koatty is `App.ts`, which looks like this:\n\n```typescript\nimport { Koatty, Bootstrap } from \"koatty\";\n// import * as path from \"path\";\n\n@Bootstrap(\n  // bootstrap function\n  // (app: any) =\u003e {\n  // Adjust libuv thread pool size\n  // process.env.UV_THREADPOOL_SIZE = \"128\";\n  // Ignore https self-signed verification\n  // process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';\n  // }\n)\n// @ComponentScan('./')\n// @ConfigurationScan('./config')\nexport class App extends Koatty {\n  public init() {\n    // this.appDebug = true; // Set debug mode to false in production environment\n  }\n}\n```\n\nThe `App` class inherits from `Koa.Application`. Therefore, an instance of `App` is an extended instance of `koa` (extended). Koatty defines the project entry through the `@Bootstrap()` decorator. The `@Bootstrap()` decorator can accept a function as a parameter, which is executed after initializing environmental parameters when the project is loaded.\n\nKoatty uses the `@ComponentScan()` decorator to define the project directory. If the project directory is modified, the relative directory name of the project needs to be passed; if certain directories are excluded from automatic loading of beans, the files that should not be automatically loaded can be placed outside the project directory.\n\nKoatty uses the `@ConfigurationScan()` decorator to customize the project configuration directory. The default value is `./config`, which is the `config` subdirectory under the project directory.\n\n## Basic Objects\n\n### App\n\n`App` is a global application object. Only one instance will be instantiated in an application, inheriting from `Koa.Application`. We can mount some global methods and objects on the `App` object. It is easy to extend the `App` object in plugins or applications.\n\nIn `CONTROLLER`, `SERVICE`, `COMPONENT` type beans, the `App` object is injected by default and can be used directly:\n\n```typescript\n@Controller()\nexport class TestController {\n  ...\n  test() {\n    // Print app object\n    console.log(this.app);\n  }\n}\n```\n\nIn `MIDDLEWARE` type beans, the `App` object is passed as a function argument:\n\n```typescript\n@Middleware()\nexport class TestMiddleware {\n  run(options: any, app: Koatty) {\n    ...\n    // Print app object\n    console.log(app);\n  }\n}\n```\n\n### Ctx\n\n`Ctx` is a request-level object, inheriting from `Koa.Context`. Every time a user request is received, the framework instantiates a `Ctx` object, which encapsulates the information of this user request and provides many convenient methods to get request parameters or set response information.\n\nIn `CONTROLLER` type beans, the `Ctx` object is a member property and can be used directly:\n\n```typescript\n@Controller()\nexport class TestController {\n  ...\n  test() {\n    // Print ctx object\n    console.log(this.ctx);\n  }\n}\n```\n\nIn `MIDDLEWARE` type beans, the `Ctx` object is passed as an argument to the middleware execution function:\n\n```typescript\n@Middleware()\nexport class TestMiddleware {\n  run(options: any, app: Koatty) {\n    ...\n    return async function (ctx: any, next: any) {\n      // Print ctx object\n      console.log(ctx);\n      return next();\n    };\n  }\n}\n```\n\nIn `SERVICE`, `COMPONENT` type beans, the `Ctx` object needs to be passed manually:\n\n```typescript\n@Service()\nexport class RequestService {\n  app: App;\n  Test(ctx: KoattyContext) {\n    // Print ctx object\n    console.log(ctx);\n  }\n}\n```\n\nNote the difference between `app.context` and `context.app`: `app.context` is a prototype for the context object created for each request. Each time a request is received, Koa creates a new context object for that request and assigns it to the current `ctx` variable. Although each request's context is based on `app.context`, this does not mean that `app.context` will be overwritten. `app.context` is actually a template for generating new context instances. The context contains a reference to `app`, but the relationship between them is unidirectional (the context accesses `app` through properties, but not vice versa).\n\n## Configuration\n\nIn actual projects, various configurations are definitely needed, including framework-required configurations and project-customized configurations. Koatty manages all configurations uniformly and divides them into different configuration files according to different functions.\n\n### Common Configurations\n\n- `config.ts`: General configurations\n- `db.ts`: Database configurations\n- `router.ts`: Router configurations\n- `middleware.ts`: Middleware configurations\n- `plugin.ts`: Plugin configurations\n\nIn addition to the common configuration files mentioned above, Koatty also supports custom configuration file naming.\n\n### Custom Configuration Scan Path\n\nConfiguration files are placed in the `src/config/` directory by default. You can also customize the configuration scan path in the entry file `App.ts`:\n\n```typescript\n@ConfigurationScan('./myconfig')\nexport class App extends Koatty {\n  public init() {\n    ...\n  }\n}\n```\n\nWhen Koatty starts, it will automatically scan all `.ts` files in the project `src/myconfig` directory and load them as configurations according to the filename.\n\n### Configuration File Format\n\nKoatty's configuration files must be exported in standard ES6 Module format, otherwise they will not be loaded. The format is as follows:\n\n```javascript\nexport default {\n  ...\n  aa: \"bb\",\n  cc: {\n    dd: \"\"\n  }\n  ...\n}\n```\n\n### Reading Configurations\n\nThere are two ways to read configurations conveniently in the project: Method One (using `app.config` function):\n\n```typescript\n...\nconst conf: Test = this.app.config(\"test\");\n```\n\nMethod Two (using decorator injection, recommended usage):\n\n```typescript\n@Controller()\nexport class AdminController {\n  @Config(\"test\")\n  conf: Test;\n}\n```\n\nThe configuration type `Test` in the above configuration reading code is a defined configuration class. Of course, you can also use `Object` or `any` types.\n\n### Configuration Classification and Hierarchy\n\nWhen Koatty scans the configuration file directory at startup, it classifies configurations according to filenames. For example, after loading `db.ts`, the configuration items in the file need to have a type added:\n\n```typescript\n// The second parameter of the config function is the configuration type\nconst conf: Test = this.app.config(\"test\", \"db\");\n```\n\nOr\n\n```typescript\n@Config(\"test\", \"db\")\nconf: Test;\n```\n\nThe default classification of configurations is `config`, so configuration items in `config.ts` do not need to fill in the type parameter.\n\nKoatty supports configuration hierarchy when reading configurations. For example, in the configuration file `db.ts`:\n\n```javascript\nexport default {\n  /* database config */\n  database: {\n    db_type: 'mysql', // support postgresql, mysql...\n    db_host: '127.0.0.1',\n    db_port: 3306,\n    db_name: 'test',\n    db_user: 'test',\n    db_pwd: '',\n    db_prefix: '',\n    db_charset: 'utf8'\n  }\n}\n```\n\nTo read the value of `db_host`:\n\n```typescript\n@Config(\"database.db_host\", \"db\")\ndbHost: string;\n```\n\nOr\n\n```typescript\nconst dbHost: string = this.app.config(\"database.db_host\", \"db\");\n```\n\nIt should be noted that hierarchical configurations only support direct access to the second level. For deeper levels, assign the value to a variable and retrieve it again:\n\n```javascript\n// config\nexport default {\n  test: {\n    bb: {\n      cc: 1\n    }\n  }\n}\nconst conf: any = this.app.config(\"test\");\nconst cc: number = conf.bb.cc;\n```\n\n### Runtime Environment Configuration\n\nKoatty can automatically recognize the current runtime environment and load corresponding configurations (if available) according to the runtime environment.\n\nThe runtime environment is defined by three properties:\n\n- `appDebug`: Defined in the constructor method (`init`) of the project entry file.\n  \n  ```typescript\n  @Bootstrap()\n  export class App extends Koatty {\n    public init() {\n      // appDebug is true for development mode\n      // appDebug is false for production mode\n      this.appDebug = false;\n    }\n  }\n  ```\n\n- `process.env.NODE_ENV`: The runtime environment variable of Node.js, which can be defined in the system environment or in the startup function of the project entry file.\n\n- `process.env.KOATTY_ENV`: The runtime environment variable of the Koatty framework.\n\nThe relationship and difference between the three variables:\n| Variable | Value | Description | Priority |\n|----------|-------|-------------|----------|\n| `appDebug` | `true/false` | Debug mode | High |\n| `process.env.KOATTY_ENV` | `development/production` | Framework runtime environment variable | Medium |\n| `process.env.NODE_ENV` | `development/production` | Node.js runtime environment variable | Low |\n\nThe priority here refers to the priority of loading runtime configurations. Higher priority configurations will override lower priority configurations.\n\n`app.env = process.env.KOATTY_ENV || process.env.NODE_ENV;`\nIf `app.env = production`, `koatty_config` will automatically load configuration files with `_pro.ts` or `_production.ts` suffixes. If `app.env = development`, it will automatically load configuration files with `_dev.ts` or `_development.ts` suffixes.\n\nFor example:\n\n```bash\n# Automatically loads config_dev.ts or config_development.ts\nNODE_ENV=dev ts-node \"test/test.ts\"\n```\n\nThrough flexible configuration of these three variables, diverse runtime environments and configurations can be supported.\n\n### Command Line Arguments\n\nKoatty can automatically recognize command line arguments and automatically fill them into corresponding configuration items:\n\n```bash\n# Automatically fills the value of config.cc.dd.ee\nNODE_ENV=dev ts-node \"test/test.ts\" --config.cc.dd.ee=77\n```\n\n### Placeholder Variable Replacement\n\nKoatty can automatically replace configuration items in configuration files identified by `${}` placeholders with values of the same name in `process.env`:\n\n```typescript\n// Automatically fills the value of ff_value\nexport default {\n  ...\n  ff: \"${ff_value}\"\n  ...\n}\nNODE_ENV=dev ff_value=999 ts-node \"test/test.ts\"\n```\n\n### Common Environment Variables\n\n- `process.env.ROOT_PATH`: The root directory defined by Koatty. Can be used anywhere in the project.\n- `process.env.APP_PATH`: The application directory defined by Koatty (in debug mode, it is `/projectDIR/src`; in production mode, it is `/projectDIR/dist`). Can be used anywhere in the project.\n- `process.env.KOATTY_PATH`: The root directory of the Koatty framework (`/projectDIR/node_modules/koatty/`). Can be used anywhere in the project.\n- `process.env.LOGS_PATH`: The directory for saving logs (default is `/projectDIR/logs`, which can be modified in the configuration). Can be used anywhere in the project.\n\n## Routing\n\nKoatty encapsulates a routing library `koatty_router` specifically for handling routes, supporting HTTP1/2, WebSocket, gRPC, and other protocol types.\n\n### Controller Routing\n\nThe `@Controller()` decorator's parameter serves as the controller's access entry, with the default value being `/`. Then, iterate over the controller's methods and register route mappings using decorators such as `GetMapping`, `DeleteMapping`, `PutMapping`, `PostMapping`.\n\nFor example:\n\n```typescript\n@Controller(\"/admin\")\nexport class AdminController {\n  ...\n  @GetMapping(\"/test\")\n  test() {\n    ...\n  }\n  ...\n}\n```\n\nThe above code registers the route `/admin/test` to `AdminController.test()`.\n\nNote: In gRPC services, the route bound by `@Controller` must match the `serviceName` defined in the proto.\n\nFor example, `@Controller(\"/Book\")` binds to the service `Book` in the proto.\n\n### Method Routing\n\nUsed to bind routes to controller methods. Refer to the decorator section for details.\n\nThe method routing decorators are `@GetMapping`, `@PostMapping`, `@DeleteMapping`, `@PutMapping`, `@PatchMapping`, `@OptionsMapping`, `@HeadMapping`, `@RequestMapping`.\n\nNote: In gRPC services, please use `@PostMapping` or `@RequestMapping` for binding, and the `path` in `@RequestMapping` must match the method name defined in the proto; in WebSocket services, please use `@GetMapping` or `@RequestMapping` for binding.\n\n### Parameter Binding\n\nIn method routing, there is a special parameter route that can easily implement RESTful APIs.\n\n```typescript\n@Controller(\"/admin\")\nexport class AdminController {\n  ...\n  @GetMapping(\"/test/:id\") // Declare parameters in the method decorator\n  test(@PathVariable(\"id\") id: number) { // Use PathVariable to get the bound parameter\n    ...\n  }\n  ...\n}\n```\n\nKoatty's routing component `koatty_router` is based on `@koa/router` (except for gRPC), and detailed routing tutorials can be found in `@koa/router`.\n\n### Route Configuration\n\nThe custom route configuration is stored in `src/config/router.ts`, which initializes the routing instance.\n\n```typescript\nexport default {\n  prefix: string;\n  methods?: string[];\n  routerPath?: string;\n  sensitive?: boolean;\n  strict?: boolean;\n};\n\n// If the project protocol is grpc, the proto file path needs to be defined:\nexport default {\n  // prefix: string;\n  // methods?: string[];\n  // routerPath?: string;\n  // sensitive?: boolean;\n  // strict?: boolean;\n  ext: {\n    protoFile: \"\", // gRPC proto file\n  };\n};\n```\n\n### Route Features\n\n- The `@Controller()` decorator has two roles: declaring the type of the bean as a controller and binding the controller route. If no path is specified when using the `@Controller()` decorator (no parameters), the default value is `/`.\n- Method routing decorators can be added multiple times to the same method. However, the `@Controller()` decorator can only be used once per class.\n- If duplicate routes are bound, the first loaded route rule takes effect according to the loading order of the controller class in the IOC container. This issue needs attention. In future versions, priority features may be added to control this.\n\nRoutes support regular expressions and parameter binding (not available in gRPC services). Detailed routing tutorials can be found in `@koa/router`.\n\n## ## Middleware\n\nKoatty is built on top of Koa, so the form of middleware in Koatty is essentially the same as in Koa, which is based on the onion model. Every time we write a middleware, it is like wrapping another layer around the onion.\n\nKoatty's framework defaults to loading middleware such as trace and payload, which can meet most web application scenarios. Users can also add their own middleware for extension.\n\nDifferent from Koa middleware, Koatty middleware is written in the form of classes and uses the `@Middleware` decorator to declare the component type.\n\nMiddleware classes must contain a method named `run(options: any, app: App)`. This method is called when the application starts and returns a function `(ctx: any, next: any){}`, which is the format of the Koa middleware.\n\n### Using Middleware\n\nUse the command line tool `koatty_cli` to execute commands in the command line:\n\n```bash\n# Create a custom middleware named jwt\nkt middleware jwt\n```\n\nThis will automatically generate a file `src/middleware/JwtMiddleware.ts` in the project directory with the generated middleware code template:\n\n```typescript\n/**\n* Middleware\n* @return\n*/\nimport { Middleware, Helper } from \"koatty\";\nimport { App } from '../App';\n\n@Middleware()\nexport class JwtMiddleware {\n  run(options: any, app: App) {\n    // Logic before returning middleware, e.g., reading configuration, etc.\n    ...\n    return function (ctx: any, next: any) {\n      // Implement middleware logic here\n      ...\n    }\n  }\n}\n```\n\nModify the project middleware configuration in `src/config/middleware.ts`:\n\n```typescript\nlist: ['JwtMiddleware'], // List of loaded middleware\nconfig: { // Middleware configuration\n  JwtMiddleware: {\n    // Middleware configuration items\n  }\n}\n```\n\n### Disabling Middleware\n\nTo disable middleware developed by the project, simply modify the middleware configuration file:\n\n```typescript\nlist: [], // If PassportMiddleware is not in the list, the Passport middleware will not execute\nconfig: { // Middleware configuration\n  'PassportMiddleware': {...},\n}\n```\n\n### Using Koa Middleware\n\nKoatty supports using Koa middleware (including middleware for Koa 1.x and 2.x):\n\n```typescript\nconst passport = require('koa-passport');\n\n@Middleware()\nexport class PassportMiddleware {\n  run(options: any, app: App) {\n    return passport.initialize();\n  }\n}\n```\n\nMount and configure usage:\n\n```typescript\nlist: ['PassportMiddleware'], // List of loaded middleware\nconfig: { // Middleware configuration\n  'PassportMiddleware': {\n    // Middleware configuration items\n  }\n}\n```\n\n### Using Express Middleware\n\nKoatty is compatible with Express middleware, and the usage is the same as Koa middleware. The framework will automatically recognize and convert for compatibility.\n\n### Middleware for Non-HTTP/S Protocols\n\nIf the project uses protocols such as `grpc`, `ws`, `wss`, etc., middleware needs to be aware that some properties of `ctx` will be inconsistent. For example, `ctx.header` does not exist in `grpc`. The specific available properties will be explained in the gRPC and WebSocket chapters.\n\n## Controller\n\nKoatty controller classes use the `@Controller()` decorator to declare them, and the parameter of this decorator is used to bind the controller's access route, with the default value being `/`. Controller classes are placed in the project's `src/controller` folder by default and support subfolders for classification. Koatty controller classes must implement the `IController` interface.\n\n### Creating Controllers\n\nUse the `koatty_cli` command line tool:\n\n- Single module mode:\n  \n  ```bash\n  kt controller index # Default http protocol\n  kt controller -t http index\n  kt controller -t grpc index\n  kt controller -t ws index\n  ```\n  \n  This will automatically create `src/controller/IndexController.ts`.\n\n- Multi-module mode:\n  \n  ```bash\n  kt controller admin/index\n  ```\n  \n  This will automatically create `src/controller/Admin/IndexController.ts`.\n\nThe template code for the controller is as follows:\n\n```typescript\nimport { Controller, GetMapping } from \"koatty\";\nimport { App } from '../../App';\n\n@Controller(\"/\")\nexport class IndexController {\n  app: App;\n  ctx: KoattyContext;\n\n  /**\n  * constructor\n  *\n  */\n  constructor(ctx: KoattyContext) {\n    this.ctx = ctx;\n  }\n\n  @GetMapping(\"/\")\n  index() {\n    return this.ok('Hello, Koatty!');\n  }\n}\n```\n\n### Controller Features\n\nController classes must implement the `IController` interface.\nThe constructor method of the controller class must have `ctx: KoattyContext` as the first parameter and assign it to the `ctx` property in the constructor method:\n\n```typescript\nconstructor(ctx: KoattyContext) {\n  this.ctx = ctx;\n}\n```\n\nAccording to the software layered architecture, controllers should not be called by other controllers (if indeed needed, move the logic to the Service layer for code reuse), nor should they be referenced by other components (anti-pattern).\n\n### Getting Parameters\n\nKoatty parses and processes request parameters, and in the controller, we can obtain parameter values through the following methods:\n\n#### Query String Parameters\n\n- Using `@Get` decorator:\n  \n  ```typescript\n  ...\n  @GetMapping(\"/get\")\n  async get(@Get(\"id\") id: number): Promise\u003cany\u003e {\n    console.log(id);\n  }\n  ...\n  ```\n\n- Using `@RequestParam` decorator:\n  \n  ```typescript\n  ...\n  @GetMapping(\"/get\")\n  async get(@RequestParam(\"id\") id: number): Promise\u003cany\u003e {\n    console.log(id);\n  }\n  ...\n  ```\n\n- Using `ctx.query`:\n  \n  ```typescript\n  ...\n  @GetMapping(\"/get\")\n  async get(): Promise\u003cany\u003e {\n    console.log(this.ctx.query[\"id\"]);\n  }\n  ...\n  ```\n\n#### RESTful API Parameters\n\n- Using `@PathVariable` decorator:\n  \n  ```typescript\n  ...\n  @GetMapping(\"/test/:id\") // Declare parameters in the method decorator\n  test(@PathVariable(\"id\") id: number) { // Use PathVariable to get the bound parameter\n    ...\n  }\n  ...\n  ```\n\n- Using `ctx.requestParam`:\n  \n  ```typescript\n  ...\n  @GetMapping(\"/test/:id\") // Declare parameters in the method decorator\n  test() { // Use PathVariable to get the bound parameter\n    console.log(this.ctx.requestParam[\"id\"]);\n  }\n  ...\n  ```\n\n#### Body Parameters\n\n- Using `@Post` decorator:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(@Post(\"id\") id: number): Promise\u003cany\u003e {\n    console.log(id);\n  }\n  ...\n  ```\n\n- Using `@RequestBody` decorator:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(@RequestBody() body: any): Promise\u003cany\u003e {\n    console.log(body.post);\n  }\n  ...\n  ```\n\n- Using `ctx.requestBody`:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(): Promise\u003cany\u003e {\n    console.log(ctx.requestBody.post);\n  }\n  ...\n  ```\n\n#### File Upload\n\n- Using `@File` decorator:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(@File(\"filename\") fileObject: any): Promise\u003cany\u003e {\n    console.log(fileObject);\n  }\n  ...\n  ```\n\n- Using `@RequestBody` decorator:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(@RequestBody() body: any): Promise\u003cany\u003e {\n    console.log(body.file);\n  }\n  ...\n  ```\n\n- Using `ctx.requestBody`:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/post\")\n  async post(): Promise\u003cany\u003e {\n    console.log(ctx.requestBody.file);\n  }\n  ...\n  ```\n\n#### HTTP Header\n\n- Using `@Header` decorator:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/get\")\n  async get(@Header(\"x-access-token\") token: string): Promise\u003cany\u003e {\n    console.log(token);\n  }\n  ...\n  ```\n\n- Using `ctx.get`:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/get\")\n  async get(): Promise\u003cany\u003e {\n    const token = this.ctx.get(\"x-access-token\");\n    console.log(token);\n  }\n  ...\n  ```\n\n- Using `ctx.header`:\n  \n  ```typescript\n  ...\n  @PostMapping(\"/get\")\n  async get(): Promise\u003cany\u003e {\n    console.log(this.ctx.header);\n  }\n  ...\n  ```\n\n### Access Control\n\nClass references follow TypeScript's scope `private | protected | public`. If not explicitly declared, the scope of class methods is `public`.\nAs long as a controller class method is bound to a route, the method can be accessed via URL mapping (even if the method's scope is not `public`). This is because currently, it is not possible to obtain the method's scope keyword through reflection (if you know how, please let me know), and URL access scope control has not been implemented.\n\n### Controller Properties and Methods\n\nRefer to the `BaseController API` for controller properties and methods.\n\n## Service Layer\n\nIn simple terms, a Service is an abstraction layer used for encapsulating business logic in complex business scenarios. The benefits of providing this abstraction are:\n\n- Keeping the logic in the Controller simpler.\n- Maintaining the independence of business logic, abstracted Services can be called repeatedly by multiple Controllers.\n- Separating logic from presentation, making it easier to write test cases.\n\nKoatty service classes use the `@Service()` decorator to declare them. Service classes are placed in the project's `src/service` folder by default and support subfolders for classification. Koatty service classes must implement the `IService` interface.\n\n### Creating Service Classes\n\nUse the `koatty_cli` command line tool:\n\n```bash\nkt service test\n```\n\nThis will automatically create `src/service/test.js`, with the generated template code:\n\n```typescript\nimport { Service, Autowired, Scheduled, Cacheable } from \"koatty\";\nimport { App } from '../App';\n\n@Service()\nexport class TestService {\n  app: App;\n  // Implement test method\n  test(name: string) {\n    return name;\n  }\n}\n```\n\n### Using Service Classes\n\nInject through decorators:\n\n```typescript\n@Autowired()\ntestService: TestService;\n```\n\nGet through the IOC container:\n\n```typescript\nthis.testService = IOCContainer.get(\"TestService\", \"SERVICE\");\n```\n\nCall the service class method:\n\n```typescript\nthis.testService.test();\n```\n\n## Persistence Layer\n\nThe persistence layer is responsible for persisting business objects from the service layer into the database. ORM encapsulates database access operations, directly mapping objects to the database.\n\nThe persistence layer is a type of business logic layering and is not mandatory in the framework. The persistence layer is of type `COMPONENT` in the framework's IOC container. It is loaded together with plugins when the framework starts. Plugins can reference the persistence layer.\n\nKoatty currently supports TypeORM by default. If you need to use other types of ORMs, such as Sequelize or Mongoose, you can refer to the `koatty_typeorm` plugin to implement it yourself.\n\n### Creating Model Classes\n\nCreate data models and entities using the `koatty_cli` command line tool:\n\n```bash\nkt model test\n```\n\nThis tool will automatically create an entity class `UserEntity` and a model class `UserModel`:\n\n```typescript\n@Component()\n@Entity('user') // Corresponds to the database table name\nexport class UserEntity extends BaseEntity {\n  @PrimaryGeneratedColumn()\n  id: number;\n  @Column()\n  name: string;\n  @CreateDateColumn()\n  createdDate: Date;\n  @UpdateDateColumn()\n  updatedDate: Date;\n}\n```\n\nThe model class `UserModel` has already generated common CURD data operation methods, including pagination.\nIn addition, the `koatty_typeorm` plugin will be automatically introduced in the `plugin` directory, which needs to be loaded in the plugin list.\n\n### Using Model Classes\n\nInject through decorators:\n\n```typescript\n@Autowired()\nuserModel: UserModel;\n```\n\nGet through the IOC container:\n\n```typescript\nthis.userModel = IOCContainer.get(\"UserModel\", \"COMPONENT\");\n```\n\nCall the model class method:\n\n```typescript\nthis.userModel.Find();\n```\n\n### Configuration\n\nModify the database-related configuration items in the project's `plugin` configuration `config/plugin.ts`:\n\n```typescript\n// src/config/plugin.ts\nexport default {\n  list: ['TypeormPlugin'], // List of loaded plugins, execution order according to array element order\n  config: { // Plugin configuration\n    TypeormPlugin: {\n      // Default configuration items\n      \"type\": \"mysql\", // mysql, mariadb, postgres, sqlite, mssql, oracle, mongodb,\n      host: \"127.0.0.1\",\n      port: 3306,\n      username: \"test\",\n      password: \"test\",\n      database: \"test\",\n      \"synchronize\": false, // true means entities will synchronize with the database every time the application runs\n      \"logging\": true,\n      \"entities\": [`${process.env.APP_PATH}/model/*`],\n      \"entityPrefix\": \"\"\n    },\n  },\n};\n```\n\nFor easier management, we can also unify the database configuration to `config/db.ts` (you need to delete the `TypeormPlugin` configuration in `config/plugin.ts`):\n\n```typescript\nexport default {\n  /* database config */\n  \"DataBase\": { // used koatty_typeorm\n    // Default configuration items\n    \"type\": \"mysql\", // mysql, mariadb, postgres, sqlite, mssql, oracle, mongodb,\n    host: \"${mysql_host}\",\n    port: \"${mysql_port}\",\n    username: \"${mysql_user}\",\n    password: \"${mysql_pass}\",\n    database: \"${mysql_database}\",\n    \"synchronize\": false, // true means entities will synchronize with the database every time the application runs\n    \"logging\": true,\n    \"entities\": [`${process.env.APP_PATH}/model/*`],\n    \"entityPrefix\": \"\"\n  },\n  \"CacheStore\": {\n    type: \"memory\", // redis or memory\n    // key_prefix: \"koatty\",\n    // host: '127.0.0.1',\n    // port: 6379,\n    // name: \"\",\n    // username: \"\",\n    // password: \"\",\n    // db: 0,\n    // timeout: 30,\n    // pool_size: 10,\n    // conn_timeout: 30\n  },\n};\n```\n\n## Plugins\n\nThe plugin mechanism is to manage and orchestrate relatively independent business logic on the premise of ensuring that the core of the framework is sufficiently lean and stable.\n\n### Why Do We Need Plugins?\n\nIn the process of using middleware, we found some problems:\n\n- The positioning of middleware is to intercept user requests and do something before and after them, such as authentication, security checks, access logs, etc. However, in reality, some functions are unrelated to requests, such as scheduled tasks, message subscriptions, background logic, etc.\n- Some functions contain very complex initialization logic that needs to be completed when the application starts. This is obviously not suitable for implementation in middleware.\n\nIn summary, we need a more powerful mechanism to manage and orchestrate those relatively independent business logics. Typical application scenarios include service registration discovery, pulling configurations from the configuration center, etc.\n\nIn the framework's IOC container, plugins are a special type of `COMPONENT`.\n\nPlugins should try to maintain independence and not couple with other components.\nIf necessary, plugins can call the persistence layer (operate databases and caches, etc.). However, they cannot call the service layer, middleware, or controllers, nor can they be called by other components.\n\n### Creating Plugins\n\nPlugins are generally reused through npm modules:\n\n```bash\nnpm i koatty_apollo --save\n```\n\nUse `koatty_cli` to create a plugin class in the application:\n\n```bash\nkt plugin apollo\n```\n\nThis will generate the plugin code template:\n\n```typescript\nimport { Plugin, IPlugin } from 'koatty';\nimport { App } from '../App';\nimport { Apollo } from 'koatty_apollo';\n\n@Plugin()\nexport class ApolloPlugin {\n  run(options: any, app: App) {\n    return Apollo(options, app);\n  }\n}\n```\n\nThen declare it in the application's `config/plugin.ts`:\n\n```typescript\nlist: ['ApolloPlugin'], // List of loaded plugins\nconfig: { // Plugin configuration\n  'ApolloPlugin': {\n    // Plugin configuration items\n  }\n}\n```\n\n### Disabling Plugins\n\nTo disable a plugin in the project, simply modify the plugin configuration file:\n\n```typescript\nlist: [], // If ApolloPlugin is not in the list, the ApolloPlugin plugin will not execute\nconfig: { // Plugin configuration\n  'ApolloPlugin': {...},\n}\n```\n\n## Advanced Applications\n\n### Parameter Validation\n\nParameter validation is a very common function in projects, and Koatty has specially encapsulated a library `koatty_validation`, which can be easily used in projects. Koatty provides two schemes for parameter validation, suitable for different scenarios:\n\n#### Scheme One: Decorators `@Valid` and `@Validated`\n\n- `@Valid` and `@Validated` decorators are only applicable to controller classes.\n  \n  ```typescript\n  @RequestMapping('/')\n  // Check if the input parameter is an email\n  index(@RequestBody() @Valid(\"IsEmail\") body: string): Promise\u003cany\u003e {\n    return this.ok('Hi Koatty');\n  }\n  ```\n\n- The `@Validated` decorator needs to be used with a DTO class:\n  \n  ```typescript\n  @RequestMapping('/SayHello')\n  @Validated() // DTO parameter validation decorator\n  SayHello(@RequestBody() params: SayHelloRequestDto): Promise\u003cSayHelloReplyDto\u003e {\n    const res = new SayHelloReplyDto();\n    return Promise.resolve(res);\n  }\n  ```\n\nUse the cli tool to create a DTO class:\n\n```bash\nkt dto SayHelloRequest\n```\n\nAdd validation rules to the DTO class:\n\n```typescript\n@Component()\nexport class SayHelloRequestDto {\n  @IsNotEmpty({ message: \"Phone number cannot be empty\" })\n  phoneNum: string;\n  ...\n}\n```\n\n#### Scheme Two: `FunctionValidator` and `ClassValidator`\n\nFor bean parameter validation in non-controller types, we can use `FunctionValidator` and `ClassValidator`.\n\n- `FunctionValidator`:\n  \n  ```typescript\n  // Throw an error directly\n  FunctionValidator.IsNotEmpty(str, \"cannot be empty\");\n  FunctionValidator.Contains(str, {message: \"must contain s\", value: \"s\"});\n  // Return true or false\n  if (ValidFuncs.IsEmail(str)) {\n    ...\n  }\n  ```\n\n- `ClassValidator`:\n  \n  ```typescript\n  class SchemaClass {\n    @IsDefined\n    id: number;\n    @IsNotEmpty\n    name: string;\n  }\n  const ins = new SchemaClass();\n  ins.name = \"\";\n  ClassValidator.valid(SchemaClass, ins, true).catch(err =\u003e {\n    console.log(err);\n  })\n  ```\n\n### Validation Rules\n\n`koatty_validation` defines a series of common validation rules.\nIn addition to built-in rules, custom function validation can also be defined:\n\nUse custom functions with the `@Valid` decorator:\n\n\n\n\n```typescript\n@Controller('/api/login')\nexport class LoginController {\n  ...\n  @Validated()\n  async GetSignout(@Post() someObj: ObjectDto) {\n    // do something\n  }\n  ...\n}\n```\n\n  // class ObjectDto\n\n```typescript\nexport class ObjectDto {\n  ...\n  @CheckFunc((value: unknown) =\u003e {\n    return value !== undefined \u0026\u0026 value !== null;\n  }, { message: \"Username cannot be empty\" })\n  username: string;\n  ...\n}\n```\n\n- Custom validation in DTO classes:\n  \n  ```typescript\n  @Controller('/api/login')\n  export class LoginController {\n    ...\n    @Validated()\n    async GetSignout(@Post() someObj: ObjectDto) {\n      // Call valid()\n      if (!someObj.validUserName()) {\n        throw new Exception(\"User is disabled\", 1004, 200);\n      }\n    }\n    ...\n  }\n  ```\n  \n  // class ObjectDto\n  \n  ```typescript\n  export class ObjectDto {\n    ...\n    @IsDefined()\n    username: string;\n    validUserName(): boolean {\n      return this.username === \"test\";\n    }\n    ...\n  }\n  ```\n\n## Exception Handling\n\nKoatty framework encapsulates the `koatty_exception` component for handling errors that need to be thrown in the project, supporting the customization of exception classes to handle different business exceptions.\n\n### Standardizing Error Throwing\n\nCustomize HTTP status, business error codes, and error messages.\nSave the error stack in logs.\n\n### Default Exception Handling\n\nIf no custom exception handling is defined in the application, exceptions thrown during program execution will be intercepted and handled uniformly by the framework's default interception mechanism. For example, throwing `Error`, the framework can still intercept it.\n\n```typescript\n// res: {\"code\":1,\"message\":\"error\"}\nthrow new Error(\"error\");\n// res: {\"code\":1000,\"message\":\"error\"}\nthrow new Exception(\"error\", 1000);\n// res: {\"code\":1000,\"message\":\"error\"}\nctx.throw(\"error\", 1000);\n```\n\n### Custom Exception Handling\n\nWe can customize exception handling classes, which need to inherit from the `Exception` base class:\n\n```typescript\n@ExceptionHandler() // Register global exception handling\nexport class BusinessException1 extends Exception {\n  // Handle exceptions uniformly in the handler\n  async handler(ctx: KoattyContext): Promise\u003cany\u003e {\n    // Return ctx.res.end for http protocol, handle based on ctx.protocol for gRPC protocol\n    return ctx.res.end(this.message);\n  }\n}\n\nexport class BusinessException2 extends Exception {\n  // Handle exceptions uniformly in the handler\n  async handler(ctx: KoattyContext): Promise\u003cany\u003e {\n    // Return ctx.res.end for http protocol, handle based on ctx.protocol for gRPC protocol\n    return ctx.res.end({code: this.code, message: this.message});\n  }\n}\n```\n\nIn the application code, we can throw different exceptions according to business logic:\n\n```typescript\n// res: {\"code\":1,\"message\":\"error\"}\nthrow new BusinessException1(\"error\");\n// res: {\"code\":1000,\"message\":\"error\"}\nthrow new BusinessException2(\"error\", 1000);\n```\n\n### Global Exception Handling\n\nKoatty provides a decorator `@ExceptionHandler()` to register global exception handling.\n\n```typescript\n@ExceptionHandler() // Register global exception handling\nexport class BusinessException extends Exception {\n  // Handle exceptions uniformly in the handler\n  async handler(ctx: KoattyContext): Promise\u003cany\u003e {\n    // Return ctx.res.end for http protocol, handle based on ctx.protocol for gRPC protocol\n    return ctx.res.end(this.message);\n  }\n}\n```\n\nGlobal exception handling is registered only once, and multiple registrations will overwrite each other. After registering global exception handling, unless a different type of exception is explicitly thrown, all exceptions will be intercepted by the global exception handling class.\n\n...\n\n```typescript\nasync index(type: string) {\n  if (type == '1') {\n    // Specify BusinessException2 to handle exceptions\n    // res: {\"code\":1000,\"message\":\"error\"}\n    throw new BusinessException2(\"error\", 1000);\n  } else {\n    // Not explicitly specified, handled by global exception handling\n    // res: error\n    throw new Error(\"error\", 1000);\n  }\n}\n...\n```\n\n## Caching\n\nKoatty encapsulates a caching library `koatty_cacheable`, which supports memory and Redis storage. `koatty_cacheable` provides two decorators `CacheAble` and `CacheEvict`.\n\n### Cache Configuration\n\nCache configuration is saved in `config/db.ts`:\n\n```typescript\nexport default {\n  ...\n  \"CacheStore\": {\n    type: \"memory\", // redis or memory, memory is default\n    // key_prefix: \"koatty\",\n    // host: '127.0.0.1',\n    // port: 6379,\n    // name: \"\",\n    // username: \"\",\n    // password: \"\",\n    // db: 0,\n    // timeout: 30,\n    // pool_size: 10,\n    // conn_timeout: 30\n  },\n  ...\n};\n```\n\nThe default uses memory storage, and if Redis is needed, Redis connection-related configuration items need to be supplemented.\n\n### Cache Usage\n\n`@CacheAble(cacheName: string, {params: string[], timeout = 3600})`\nEnables automatic caching of method results. When this method is executed, it first checks the cache. If the cached result exists, it returns the result directly; otherwise, it executes and then returns and stores the result. The elements of the `params` array are parameter names, which will be used to get the parameter values, and then concatenated with the cache prefix to form the cache key. For example: `@CacheAble(\"getUser\", {params:[\"name\"]})`, if the `name` parameter value is `tom`, the concatenated cache key is `getUser:name:tom`.\n\n`@CacheEvict(cacheName: string, {params: string[], delayedDoubleDeletion = true})`\nClears the method result cache. The `params` parameter is used the same as `CacheAble`. `delayedDoubleDeletion` is `true` to enable delayed double deletion strategy.\n\n`GetCacheStore(app: Koatty)`\nGets the cache instance, which can manually call `get`, `set`, and other methods to operate the cache.\nExample:\n\n```typescript\nimport { CacheAble, CacheEvict, GetCacheStore } from \"koatty_cacheable\";\n@Service()\nexport class TestService {\n  @CacheAble(\"testCache\") // Automatically caches the result, cache key=testCache\n  getTest() {\n    // todo\n  }\n\n  @CacheEvict(\"testCache\") // Clears the cache before executing setTest(), cache key=testCache\n  setTest() {\n    // todo\n  }\n\n  test() {\n    // Manually operate the cache instance\n    const store = GetCacheStore(this.app);\n    store.set(key, value);\n  }\n}\n```\n\nNote: Decorators `@CacheAble` and `@CacheEvict` cannot be used for controller classes.\n\n## Scheduling Tasks\n\nKoatty encapsulates a scheduling task library `koatty_schedule`, which supports cron expressions and distributed locks based on Redis.\n\n### Cron Expressions\n\nCron expressions consist of 6 fields, representing seconds, minutes, hours, day of month, month, and day of week:\n\n- Seconds: 0-59\n- Minutes: 0-59\n- Hours: 0-23\n- Day of Month: 1-31\n- Months: 1-12 (Jan-Dec)\n- Day of Week: 1-7 (Mon-Sun)\n\n`@Scheduled(cron: string)`\nEasily add task execution plans to methods through the `@Scheduled` decorator:\n\n```typescript\nimport { Scheduled, RedLock } from \"koatty_schedule\";\nexport class TestService {\n  @Scheduled(\"0 * * * * *\")\n  Test() {\n    // todo\n  }\n}\n```\n\n### Task Execution Lock\n\nIn some business scenarios, scheduled tasks cannot be executed concurrently, and the solution is to add a lock. `koatty_schedule` implements a distributed lock based on Redis.\n`RedLock(name?: string, options?: RedLockOptions)`\n\n`RedLockOptions`:\n\n- `lockTimeOut?`: Lock timeout in milliseconds, default 10000\n- `retryCount?`: Maximum retry count, default 3\n- `RedisOptions`: Redis configuration, supports Standalone, Sentinel, Cluster\n\nExample:\n\n```typescript\nimport { Scheduled, RedLock } from \"koatty_schedule\";\nexport class TestService {\n  @Scheduled(\"0 * * * * *\")\n  @RedLock(\"testCron\") // locker\n  Test() {\n    // todo\n  }\n}\n```\n\nBecause Redis is used, Redis cache configuration is saved in `config/db.ts`:\n\n```typescript\nexport default {\n  ...\n  \"RedLock\": {\n    host: '127.0.0.1',\n    port: 6379,\n    name: \"\",\n    username: \"\",\n    password: \"\",\n    db: 0\n  },\n  ...\n};\n```\n\nYou can also pass in the configuration when calling the decorator:\n\n```typescript\nimport { Scheduled, RedLock } from \"koatty_schedule\";\nexport class TestService {\n  @Config(\"redisConf\", \"db\")\n  private redisConf;\n  @Scheduled(\"0 * * * * *\")\n  @RedLock(\"testCron\", {\n    RedisOptions: redisConf\n  }) // locker\n  Test() {\n    // todo\n  }\n}\n```\n\nSeveral points to note:\n\n- Decorators `@Scheduled` and `@RedLock` cannot be used for controller classes;\n- Configure corresponding parameters according to the duration of the scheduled task to prevent lock expiration.\n- When the lock expires but the business logic has not been completed, the lock will automatically extend for one time. If the extension time expires and the business logic is still not completed, the lock will be released.\n\n## gRPC\n\nKoatty supports gRPC services starting from version 3.4.x.\n\n### Proto Protocol\n\nUse the `koatty_cli` command line tool (\u003e=3.4.6):\n\n```bash\nkt proto hello\n```\n\nThis will automatically create `src/proto/Hello.proto`. Modify according to the actual situation.\n\n### gRPC Protocol Controller\n\nUse the `koatty_cli` command line tool (\u003e=3.4.6):\n\n- Single module mode:\n  \n  ```bash\n  kt controller -t grpc hello\n  ```\n  \n  This will automatically create `src/controller/HelloController.ts`.\n\n- Multi-module mode:\n  \n  ```bash\n  kt controller -t grpc admin/hello\n  ```\n  \n  This will automatically create `src/controller/Admin/HelloController.ts`.\n\nThe controller template code is as follows:\n\n```typescript\nimport { KoattyContext, Controller, Autowired, RequestMapping, RequestBody } from 'ko';\nimport { App } from '../App';\nimport { SayHelloRequestDto } from '../dto/SayHelloRequestDto';\nimport { SayHelloReplyDto } from '../dto/SayHelloReplyDto';\n\n@Controller('/Hello') // Consistent with proto.service name\nexport class HelloController {\n  app: App;\n  ctx: KoattyContext;\n\n  /**\n  * Custom constructor\n  *\n  */\n  constructor(ctx: KoattyContext) {\n    this.ctx = ctx;\n  }\n\n  /**\n  * SayHello interface\n  * Access path: grpc://127.0.0.1/Hello/SayHello\n  *\n  * @param {SayHelloRequestDto} data\n  * @returns\n  */\n  @RequestMapping('/SayHello') // Consistent with proto.service.method name\n  @Validated() // Parameter validation\n  SayHello(@RequestBody() params: SayHelloRequestDto): Promise\u003cSayHelloReplyDto\u003e {\n    const res = new SayHelloReplyDto();\n    return Promise.resolve(res);\n  }\n}\n```\n\nIn addition to the controller file, Koatty will also automatically create RPC protocol input and output DTO classes, such as the aforementioned `SayHelloRequestDto` and `SayHelloReplyDto`.\n\n### Service Configuration\n\nModify `config/config.ts`:\n\n```typescript\nexport default {\n  ...\n  protocol: \"grpc\", // Server protocol 'http' | 'https' | 'http2' | 'grpc' | 'ws' |\n  ...\n}\n```\n\nModify `config/router.ts`:\n\n```typescript\nexport default {\n  ...\n  /**\n  * Other extended configuration\n  */\n  ext: {\n    protoFile: process.env.APP_PATH + \"proto/Hello.proto\", // gRPC proto file\n  }\n  ...\n}\n```\n\nOK, now you can start a gRPC server.\n\n## WebSocket\n\nKoatty supports WebSocket services starting from version 3.4.x.\n\n### WebSocket Protocol Controller\n\nUse the `koatty_cli` command line tool (\u003e=3.4.6):\n\n- Single module mode:\n  \n  ```bash\n  kt controller -t ws requst\n  ```\n  \n  This will automatically create `src/controller/RequstController.ts`.\n\n- Multi-module mode:\n  \n  ```bash\n  kt controller -t ws admin/requst\n  ```\n  \n  This will automatically create `src/controller/Admin/RequstController.ts`.\n\nThe controller template code is as follows:\n\n```typescript\nimport { KoattyContext, Controller, Autowired, GetMapping } from 'koatty';\nimport { App } from '../App';\n// import { TestService } from '../service/TestService';\n...\n\n@Controller('/requst')\nexport class RequstController {\n  app: App;\n  ctx: KoattyContext;\n  // @Autowired()\n  // protected TestService: TestService;\n  /**\n  * Custom constructor\n  *\n  */\n  constructor(ctx: KoattyContext) {\n    this.ctx = ctx;\n  }\n\n  /**\n  * index interface\n  * Access path: ws://127.0.0.1/requst\n  *\n  * @returns\n  * @memberof RequstController\n  */\n  @RequestMapping('/')\n  index(@RequestBody() @Valid(\"IsEmail\") body: string): Promise\u003cany\u003e {\n    return this.ok('Hi Koatty');\n  }\n}\n```\n\n### Service Configuration\n\nModify `config/config.ts`:\n\n```typescript\nexport default {\n  ...\n  protocol: \"ws\", // Server protocol 'http' | 'https' | 'http2' | 'grpc' | 'ws' |\n  ...\n}\n```\n\nOK, now you can start a WebSocket server.\n\n## Event Mechanism (Event)\n\nDuring the application startup process, the `app` object in the Koatty framework defines a series of events in addition to the events inherent in Koa itself:\n\n\n![](https://cdn.jsdelivr.net/gh/Koatty/koatty_doc@master/docs/assets/event.png)Note:\n\n- The `appStart` event is triggered after the service starts.\n  We can bind to different events according to project needs. For example, in the scenario of service registration discovery, if hardware failure occurs, you can bind to the `appStop` event to handle service deregistration.\n\n```typescript\napp.once(\"appStop\", () =\u003e {\n  // Deregister service\n  ...\n})\n```\n\n### BootFunc\n\nThe role of the `@Bootstrap` decorator is to declare the project entry class, which supports passing a function as a parameter. This function will be executed first when the project starts.\n\n```typescript\n@Bootstrap(\n  // bootstrap function\n  (app: any) =\u003e {\n    // todo\n  }\n)\nexport class App extends Koatty {\n  ...\n}\n```\n\nCommon application scenarios are to handle some runtime environment settings before startup, such as `NODE_ENV`. The startup function supports asynchronous execution.\nNote: The startup function is executed after the framework's `initialize` initialization, at which point the framework's related path attributes (`appPath`, `rootPath`, etc.) and `process.env` have been loaded and set, but other components (plugins, middleware, controllers, etc.) have not been loaded. Be aware when defining the startup function.\n\n### BindEventHook\n\nIn addition to the `@Bootstrap` decorator, we can also use the `BindEventHook` custom decorator to bind application events (`appBoot`, `appReady`, `appStart`, `appStop`) to the startup class.\n\n```typescript\n// src/TestBootstrap.ts:\nexport function TestBootstrap(): ClassDecorator {\n  return (target: Function) =\u003e {\n    BindEventHook(AppEvent.appBoot, (app: Koatty) =\u003e {\n      // todo\n      return Promise.resolve();\n    }, target)\n  }\n}\n```\n\nUse on the project startup class:\n\n```typescript\n@Bootstrap()\n@TestBootstrap()\nexport class App extends Koatty {\n  ...\n}\n```\n\nNote: The function execution of the custom decorator created by `BindEventHook` is triggered by the event (`appBoot`, `appReady`, `appStart`, `appStop`), and attention should be paid to the framework startup logic and related context.\n\n### Loading Customizations\n\nThe entry class of the project can also set two other decorators, which are:\n\n- `@ComponentScan('./')`: Declares the directory of project components, default is the project `src` directory, containing all types of components.\n- `@ConfigurationScan('./config')`: Declares the directory of project configuration files, default is `src/config` directory.\n\n## IOC Container\n\nIoC stands for Inversion of Control, which translates to control inversion in English. In ES6 Class-style programming, simply creating instances and holding them reveals the following disadvantages:\n\n- To instantiate a component, you must first instantiate dependent components, leading to tight coupling.\n- Each component needs to instantiate a dependent component, without reuse.\n- Many components need to be destroyed to release resources, such as DataSource. However, if the component is shared by multiple components, how to ensure that all users have been destroyed.\n- As more components are introduced, it becomes more difficult to write shared components, and the dependency relationships between them become more complex.\n  If a system has a large number of components, if the lifecycle and interdependent relationships of these components are maintained by the components themselves, it not only greatly increases the complexity of the system but also leads to extremely tight coupling between components, which brings great difficulties to testing and maintenance.\n\nTherefore, the core issues are:\n\n1. Who is responsible for creating components?\n2. Who is responsible for assembling components according to dependency relationships?\n3. How to correctly destroy components in order of dependency when destroying?\n\nThe core solution to this problem is IoC. Referring to the implementation mechanism of Spring IoC, Koatty implements an IOC container (`koatty_container`), which automatically classifies and loads components at startup and injects corresponding dependencies according to dependency relationships. Therefore, IoC is also known as Dependency Injection (DI: Dependency Injection), which solves one of the main problems: separating the creation and configuration of components from their use, and managing the lifecycle of components by the IoC container.\n\n### Component Classification\n\nAccording to different application scenarios of components, Koatty divides Beans into four types: `COMPONENT`, `CONTROLLER`, `MIDDLEWARE`, `SERVICE`.\n\n- `COMPONENT`: Extension classes, third-party classes belong to this type, such as Plugins, ORM persistence layers, etc.\n\n- `MIDDLEWARE`: Middleware classes\n- `SERVICE`: Service classes\n\n### Component Loading\n\nThrough the core Loader of the Koatty framework, components are automatically analyzed and assembled at project startup, and the dependency issues between components are automatically handled. The IOC container provides a series of API interfaces for convenient registration and retrieval of assembled Beans.\n\n### Circular Dependencies\n\nAs the scale of the project expands, circular dependencies are easily introduced. The approach of `koatty_container` to solve circular dependencies is lazy loading. `koatty_container` binds an `appReady` event on the `app`, which is used for lazy loading of beans that produce circular dependencies. Attention needs to be paid when using IOC:\n\n```typescript\napp.emit(\"appReady\");\n```\n\nNote: Although lazy loading can solve most scenarios of circular dependencies, it may still fail to assemble in extreme cases. Solutions:\n\n1. Try to avoid circular dependencies, introduce new third-party common classes to decouple mutually dependent classes.\n2. Use the IOC container to get the prototype of the class (`getClass`) and instantiate it manually.\n\n## AOP Aspects\n\nKoatty implements an aspect-oriented programming mechanism based on the IOC container, using decorators and built-in special methods. Encapsulation is achieved through nested functions, which is simple and efficient.\n\n### Pointcut Declaration Types\n\n- Decorator declaration: Use `@Before`, `@After`, `@BeforeEach`, `@AfterEach` decorators to declare pointcuts.\n- Built-in method declaration: Use `__before`, `__after` built-in hidden methods to declare pointcuts.\n\n### Differences Between Declaration Methods\n\n- Declaration method\n  - Dependency on Aspect Aspect Class\n  - Ability to use class scope\n  - Parameter dependency of pointcut method\n  - Priority\n  - Usage restrictions\n\n**Decorator Declaration**\n\n- Dependency: Requires the creation of a corresponding Aspect aspect class to use.\n- Ability to use class scope: No\n- Parameter dependency of pointcut method: Yes, low\n- Priority: Low\n- Usable for all types of beans\n\n**Built-in Method Declaration**\n\n- Dependency: Does not depend on the Aspect aspect class.\n- Ability to use class scope: Yes\n- Parameter dependency of pointcut method: No, high\n- Only usable for `CONTROLLER` type beans, as it depends on the `this` pointer of the class.\n\nNote: If a class uses the decorator `@BeforeEach` and this class also contains the `__before` method (whether it is its own or inherited from the parent class), then the `__before` method has higher priority than the decorator, and the class's decorator `@BeforeEach` is invalid (`@AfterEach` and `__after` are the same).\n\nFor example:\n\n```typescript\n@Controller('/')\nexport class TestController {\n  app: App;\n  ctx: KoattyContext;\n  @Autowired()\n  protected TestService: TestService;\n  // Does not depend on the Aspect aspect class\n  async __before(): Promise\u003cany\u003e { // Does not depend on the specific method's parameters, obtain through this pointer\n    console.log(this.app);  // Can use class scope, obtain the current class attribute through this pointer\n    console.log(this.ctx)\n  }\n  @Before(\"TestAspect\") // Depends on TestAspect aspect class, able to get path parameter\n  async test(path: string) {\n    ...\n  }\n}\n```\n\n### Creating Aspect Classes\n\nUse `koatty_cli` to create:\n\n```bash\nkt aspect test\n```\n\nThis will automatically generate the template code:\n\n```typescript\nimport { Aspect } from \"koatty\";\nimport { App } from '../App';\n\n@Aspect()\nexport class TestAspect {\n  app: App;\n  run() {\n    console.log('TestAspect');\n  }\n}\n```\n\n### Decorators\n\n#### Class Decorators\n\n- Name\n  - Parameter\n  - Description\n  - Remarks\n\n**@Aspect()**\n\n- Identifier: Registered in the IOC container, default is the class name.\n  - Declare the current class as an aspect class. The aspect class is executed at the pointcut, and the aspect class must implement the `run` method for the pointcut to call.\n  - Only for aspect classes\n\n**@Bootstrap()**\n\n- bootFunc: Function to execute before application startup. The specific execution timing is when the `app.on(\"appReady\")` event is triggered.\n  - Declare the current class as a startup class, which is the entry file of the project.\n  - Only for startup classes\n\n**@ComponentScan()**\n\n- scanPath: String or string array\n  - Define the directory that the project needs to automatically load into the container.\n  - Only for application startup classes\n\n**@Component()**\n\n- identifier: Registered in the IOC container, default is the class name.\n  - Define the class as a component class.\n  - Used for third-party modules or imported classes\n\n**@ConfigurationScan()**\n\n- scanPath: String or string array, configuration file directory\n  - Define the directory of project configuration files.\n  - Only for application startup classes\n\n**@Controller()**\n\n- path: Binding controller access route\n  - Define the class as a controller class and bind the route. The default route is `/`.\n  - Only for controller classes\n\n**@Service()**\n\n- identifier: Registered in the IOC container, default is the class name.\n  - Define the class as a service class.\n  - Only for service classes\n\n**@Middleware()**\n\n- identifier: Registered in the IOC container, default is the class name.\n  - Define the class as a middleware class.\n  - Only for middleware classes\n\n**@ExceptionHandler()**\n\n- Define the class as a global exception handling class.\n  - Only for exception handling classes\n\n**@BeforeEach(aopName:string)**\n\n- aopName: Name of the aspect class executing the pointcut\n  - Declare an aspect for the current class, which will execute the aspect class's `run` method before each method (\"constructor\", \"init\", \"__before\", \"__after\" excepted) of the current class.\n  - Only for controller classes\n\n**@AfterEach(aopName:string)**\n\n- aopName: Name of the aspect class executing the pointcut\n  - Declare an aspect for the current class, which will execute the aspect class's `run` method after each method (\"constructor\", \"init\", \"__before\", \"__after\" excepted) of the current class.\n  - Only for controller classes\n\n#### Property Decorators\n\n- Name\n  - Parameter\n  - Description\n  - Remarks\n\n**@Autowired()**\n\n- identifier: Registered in the IOC container, default is the class name.\n  - type: Type of bean to inject\n  - constructArgs: Constructor method arguments of the injected bean. If passed, return an instance of the bean automatically injected from the IOC container to the current class.\n  - isDelay: Whether to delay loading. Delayed loading is mainly to solve circular dependency issues.\n  - Only for constructor arguments (constructor)\n\n**@Config()**\n\n- key: Key of the configuration item\n  - type: Type of the configuration item. The type of the configuration item is automatically defined according to the file where the configuration item is located, e.g., \"db\" represents the file `db.ts`.\n  - Only for configuration items\n\n**@Values()**\n\n- val: Value of the attribute, can be a function, the attribute value is the result of the function operation.\n  - defaultValue: Default value, when val is `Null`, `undefined`, `NaN`, take the default value.\n  - Used to dynamically modify the attribute value of the class instance.\n  - Only for class attributes\n\n#### Method Decorators\n\n- Name\n  - Parameter\n  - Description\n  - Remarks\n\n**@Before(aopName:string)**\n\n- aopName: Name of the aspect class executing the pointcut\n  - Declare an aspect for the current method, which will execute the aspect class's `run` method before the current method.\n  - Only for controller methods\n\n**@After(aopName:string)**\n\n- aopName: Name of the aspect class executing the pointcut\n  - Declare an aspect for the current method, which will execute the aspect class's `run` method after the current method.\n  - Only for controller methods\n\n**@RequestMapping()**\n\n- path: Bound route\n  - requestMethod: Bound HTTP request method. Use `RequestMethod` enum data for assignment, e.g., `RequestMethod.GET`.\n  - If set to `RequestMethod.ALL`, it means support all request methods\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding routes to controller methods\n  - Only for controller methods\n\n**@GetMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Get routes to controller methods\n  - Only for controller methods\n\n**@PostMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Post routes to controller methods\n  - Only for controller methods\n\n**@DeleteMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Delete routes to controller methods\n  - Only for controller methods\n\n**@PutMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Put routes to controller methods\n  - Only for controller methods\n\n**@PatchMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Patch routes to controller methods\n  - Only for controller methods\n\n**@OptionsMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Options routes to controller methods\n  - Only for controller methods\n\n**@HeadMapping()**\n\n- path: Bound route\n  - routerOptions: Configuration items of `koa/_router`\n  - Used for binding Head routes to controller methods\n  - Only for controller methods\n\n**@Scheduled()**\n\n- cron: Task scheduling configuration\n  - * * * * *\n    \n    Seconds: 0-59\n    Minutes: 0-59\n    Hours: 0-23\n    Day of Month: 1-31\n    Months: 1-12 (Jan-Dec)\n    Day of Week: 1-7 (Mon-Sun)\n  - Define the execution plan task of the class method.\n  - Cannot be used for controller methods, dependent on the `koatty_schedule` module.\n\n**@Validated()**\n\n- Used in conjunction with DTO types for parameter validation\n  - Method parameters without DTO types are not effective, only for controller classes\n\n**@RedLock()**\n\n- name: Name of the lock\n  - options: Lock configuration, including Redis server connection configuration\n  - Define that the method must first obtain a distributed lock (based on Redis) before execution.\n  - Dependent on the `koatty_schedule` module\n\n**@CacheAble()**\n\n- cacheName: Cache name\n  - paramKey: Based on method input parameters as cache key, value is the position of the method input parameter, starting from 0\n  - redisOptions: Redis server connection configuration\n  - Dependent on the `koatty_cacheable` module\n  - Cannot be used for controller methods\n\n**@CacheEvict()**\n\n- cacheName: Cache name\n  - paramKey: Based on method input parameters as cache key, value is the position of the method input parameter, starting from 0\n  - eventTime: Time point for clearing the cache\n  - redisOptions: Redis server connection configuration\n  - Used together with `@Cacheable`, for clearing the cache when the method is executed\n  - Dependent on the `koatty_cacheable` module\n  - Cannot be used for controller methods\n\n#### Parameter Decorators\n\n- Name\n  - Parameter\n  - Description\n  - Remarks\n\n**@File()**\n\n- name: File name\n  - Get the uploaded file object\n  - Only for HTTP controller method parameters\n\n**@Get()**\n\n- name: Parameter name\n  - Get querystring parameters (get route-bound parameters)\n  - Only for HTTP controller method parameters\n\n**@Header()**\n\n- name: Parameter name\n  - Get Header content\n  - Only for HTTP controller method parameters\n\n**@PathVariable()**\n\n- name: Parameter name\n  - Get route-bound parameters `/user/:id`\n  - Only for HTTP controller method parameters\n\n**@Post()**\n\n- name: Parameter name\n  - Get Post parameters\n  - Only for HTTP controller method parameters\n\n**@RequestBody()**\n\n- Get `ctx.body`\n- Only for controller method parameters\n\n**@RequestParam()**\n\n- name: Parameter name\n  - Get Get or Post parameters, Post takes precedence\n  - Only for controller method parameters\n\n**@Valid()**\n\n- rule: Validation rule, supports built-in rules or custom functions\n  - message: Error message when the rule does not match\n  - Used for parameter format validation\n  - Only for controller classes\n\n**@Inject()**\n\n- paramName: Constructor method argument name (formal parameter)\n  - cType: Type of bean to inject\n  - This decorator uses the class constructor method argument to inject dependencies. If used together with `@Autowired()`, it may overwrite the same property injected by `autowired`.\n  - Only for constructor arguments (constructor)\n\n## Programming Standards and Conventions\n\nKoatty follows the principle that conventions are more important than configuration. To standardize project code and improve robustness, some default standards and conventions have been made.\n\n### Koatty Framework and Peripheral Component Version Definition\n\n- Minor version: e.g., `1.1.1 =\u003e 1.1.2` (minor feature additions, bug fixes, etc., downward compatible with 1.1.x)\n- Middle version: e.g., `1.1.0 =\u003e 1.2.0` (larger feature additions, partial module refactoring, etc. Mainly downward compatible, may have a small number of features incompatible)\n- Major version: e.g., `1.0.0 =\u003e 2.0.0` (overall design, refactoring, etc. of the framework, not downward compatible)\n- Stable version: Even-numbered versions at the end are stable versions, odd-numbered versions are unstable versions.\n\n### Programming Style\n\nUse Class-style programming including Controller, Service, Model, etc., use `Class` instead of `function` to organize code. Excludes configurations, tools, function libraries, third-party libraries, etc.\nSingle file only exports one class\nIn the project, a single `.ts` file only exports once and exports a `Class`. Excludes configurations, tools, function libraries, third-party libraries, etc.\nClass names must be the same as file names\nPeople familiar with JAVA will not be unfamiliar with this. The class name must be the same as the file name to maintain uniqueness in the IOC container and prevent class overwriting.\nNo duplicate classes of the same type are allowed\nKoatty divides Beans in the IOC container into four types: `COMPONENT`, `CONTROLLER`, `MIDDLEWARE`, `SERVICE`.\nBeans of the same type cannot have the same class name, otherwise loading will fail.\nFor example: `src/Controller/IndexController.ts` and `src/Controller/Test/IndexController.ts` are duplicate classes.\nIt should be noted that the type of Bean is determined by the decorator, not the filename or directory name. If `IndexController.ts` is decorated with `@Service()`, then its type is `SERVICE`.\n\nFor Koatty official components, we recommend using `^` for dependency introduction, and strongly advise against locking versions.\n\n```json\n{\n  \"dependencies\": {\n    \"koatty_lib\": \"^1.0.0\"\n  }\n}\n```\n\n## Q \u0026 A\n\nComesoon...\n\n## API\n\n- `app`\n  \n  - API documentation\n\n- `ctx`\n  \n  - API documentation\n\n- `IOCContainer`\n  \n  - API documentation\n\n- Other APIs\n  \n  - API documentation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoatty%2Fkoatty_doc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoatty%2Fkoatty_doc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoatty%2Fkoatty_doc/lists"}