Controller 
Create Controller 
For example, we create a Controller student in the module demo-student:
1. Cli command 
$ vona :create:bean controller student --module=demo-student2. Menu command 
TIP
Context Menu - [Module Path]: Vona Create/Controller
Controller Definition 
@Controller<IControllerOptionsStudent>('student')
export class ControllerStudent extends BeanBase {
  @Web.post('')
  @Api.body(v.tableIdentity())
  async create(@Arg.body() student: DtoStudentCreate): Promise<TableIdentity> {
    return (await this.scope.service.student.create(student)).id;
  }
}Inherited from the
BeanBaseclassUse the
ControllerdecoratorLine 1: Controller Path:
'student'Line 3: Action Path :
''
Routes 
Like Fastify, Vona also uses find-my-way as the routing library. All Controller Actions that use the @Web decorator will be automatically registered in the routing table
The route path is composed of the Controller Path and the Action Path
General format 
Route Path = GlobalPrefix + Module Url + Controller Path + Action Path- GlobalPrefix: Project configuration: 
app.config.server.globalPrefix, default is/api - Module Url: The Url fragment generated by the module name. For example, the module Url of module demo-student is 
demo/student 
For example, the route of Controller teacher in module demo-student is as follows:
| Controller Path | Action Path | Route Path | 
|---|---|---|
| teacher | empty | /api/demo/student/teacher | 
| teacher | :id | /api/demo/student/teacher/:id | 
Simplification rule 1 
If the Controller Path is the same as the module name, the duplicate part will be automatically removed
For example, the route of Controller student in module demo-student is as follows:
| Controller Path | Action Path | Route Path | 
|---|---|---|
| student | empty | /api/demo/student | 
| student | :id | /api/demo/student/:id | 
Simplification rule 2 
If the Controller Path or Action Path is prefixed with /, then the Module Url is automatically removed
| Controller Path | Action Path | Route Path | 
|---|---|---|
| /student | empty | /api/student | 
| /student | :id | /api/student/:id | 
Simplification rule 3 
If the Controller Path or Action Path is prefixed with //, then the GlobalPrefix and Module Url are automatically removed
| Controller Path | Action Path | Route Path | 
|---|---|---|
| //student | empty | /student | 
| //student | :id | /student/:id | 
For example, the homepage of the project has the following route:
| Controller Path | Action Path | Route Path | 
|---|---|---|
| any value | // | / | 
Request Method 
Vona uses decorators to define request methods. These decorators are put into a group @Web. Because there are many decorators, the grouping mechanism can reduce the mental burden. For example: we set a post method for create:
import { Web } from 'vona-module-a-web';
class ControllerStudent {
  @Web.post()
  async create() {}
}- Method decorator list
 
| Name | Description | 
|---|---|
| @Web.post | post | 
| @Web.get | get | 
| @Web.delete | delete | 
| @Web.put | put | 
| @Web.patch | patch | 
| @Web.options | options | 
| @Web.head | head | 
Request Parameters 
We need to get various parameters from the request, such as Query, Params, Body, Headers, etc. Similarly, Vona also provides many decorators for obtaining parameters. We also put all parameter decorators into the group @Arg to reduce the mental burden. For example, if we want to obtain the data of a student, the requested URL is /?id=1:
class ControllerStudent3 {
  @Web.get()
  findOne(@Arg.query('id') id: number) {}
}Depending on the characteristics of the parameters, different types of parameters allow the specification of field names, such as specifying id in the query decorator. We can also obtain the entire query object without specifying the field name
For example, the URL is /?id=1&name=tom:
class DtoStudentInfo {
  id: number;
  name: string;
}
class ControllerStudent3 {
  @Web.get()
  findOne(@Arg.query() query: DtoStudentInfo) {
    console.log(query.id, query.name);
  }
}- Parameter decorator list
 
| Name | Description | 
|---|---|
| @Arg.param | Param | 
| @Arg.query | Query | 
| @Arg.body | Body | 
| @Arg.headers | Headers | 
| @Arg.fields | Fields | 
| @Arg.field | Field | 
| @Arg.files | Files | 
| @Arg.file | File | 
| @Arg.user | The current user | 
Parameter Validation 
Vona Based on Zod, a very concise and flexible mechanism is provided to verify request parameters
- See: Validation
 
Swagger/Openapi 
Vona implements a very convenient Swagger/Openapi based on @asteasolutions/zod-to-openapi. Swagger/Openapi in Vona still uses the decorators in Validation, and provides extension tools to set metadata related to Openapi
- See: Swagger/Openapi
 
Response Body 
Vona provides a mechanism similar to Validation, which specifies the type of the Response body and automatically generates Swagger/Openapi metadata
1. Automatically infer Zod Schema: Basic type/Dto/Entity 
If the body type is Basic type/Dto/Entity, then the system will automatically infer the corresponding Zod Schema and automatically generate Swagger/Openapi
- Example: 
string 
class ControllerStudent {
  @Web.get()
  findOne(): string {
    return 'Tom';
  }
}
- Example: 
EntityStudent 
class ControllerStudent {
  @Web.get()
  findOne(): EntityStudent {
    return {} as EntityStudent;
  }
}
- List of types that can be automatically inferred
 
| Name | Description | 
|---|---|
| string | z.string() | 
| number | z.number() | 
| boolean | z.boolean() | 
| Dto | z.object({...}) | 
| Entity | z.object({...}) | 
2. Specify Zod Schema 
We can also explicitly specify Zod Schema and automatically generate Swagger/Openapi
- Example: 
string[] 
Use the decorator @Api.body to specify Zod Schema. Zod Schema usage rules are consistent with Validation
import { Api } from 'vona-module-a-openapi';
class ControllerStudent {
  @Web.get()
  @Api.body(v.array(String))
  findOne(): string[] {
    return ['Tom'];
  }
}
- Example: 
Promise<EntityStudent> 
class ControllerStudent {
  @Web.get()
  @Api.body(EntityStudent)
  async findOne(): Promise<EntityStudent> {
    return {} as EntityStudent;
  }
}
Response Body Wrapper Object 
By default, Vona automatically provides a wrapper object for the Response body. For example, if we want to return a body of type string, the actual returned data type is:
{
  code: string;
  message: string;
  data: string;
}We can also use the decorator @Api.bodyCustom to customize the wrapper object
1. Disable the wrapper object 
You can disable the wrapper object and directly return the Response body itself
class ControllerStudent {
  @Web.get()
  @Api.bodyCustom(false)
  findOne(): string {
    return 'Tom';
  }
}
2. Provide a custom wrapper object 
- First, define the wrapper function:
 
export function bodySchemaWrapperCustom(bodySchema: any) {
  return z.object({
    status: z.number(),
    msg: z.string(),
    data: bodySchema,
  });
}- Then pass the wrapper function 
bodySchemaWrapperCustomto the decorator@Api.bodyCustom 
class ControllerStudent {
  @Web.get()
  @Api.bodyCustom(bodySchemaWrapperCustom)
  findOne(): string {
    return 'Tom';
  }
}
- If the type of Response body is 
Promise<EntityStudent>, the code is as follows: 
class ControllerStudent {
  @Web.get()
  @Api.bodyCustom(bodySchemaWrapperCustom, EntityStudent)
  async findOne(): Promise<EntityStudent> {
    return {} as EntityStudent;
  }
}
More Swagger/Openapi configuration 
1. @Api.contentType 
Set the response content type
@Api.contentType('application/xml')2. @Api.exclude 
Do not display this API in Swagger/Openapi metadata. This decorator can be used in Controller/Action
@Api.exclude()3. @Api.header/@Api.headers 
Define Request headers
@Api.header({ name: 'x-request-header', description: 'request header' })
// Define multiple headers at once
@Api.headers([{ name: 'x-request-header', description: 'request header' }])4. @Api.httpCode 
Set the response http code
@Api.httpCode(400)5. @Api.setHeader 
Set the response headers
@Api.setHeader('x-response-header', 'value')
// Set multiple headers at once
@Api.setHeader({ 'x-response-header1': 'value1', 'x-response-header2': 'value2' })6. @Api.tags 
Set the grouping information in Swagger/Openapi. This decorator can be used for Controller/Action
@Api.tags(['Student'])Action options 
1. Example 
You can pass more options when defining the request method of Action
class ControllerBook {
  @Web.get(':id', {
    tags: ['Book'],
    description: 'Find a book',
  })
  findOne(@Arg.param('id') id: number): EntityBook {}
}- tags: Set the grouping information in Swagger/Openapi
 - description: Set the description information in Swagger/Openapi
 
2. description supports i18n 
First, define the language resources:
- English: 
src/module/demo-student/src/config/locale/en-us.ts 
export default {
  FindBook: 'Find a book',
};- Chinese: 
src/module/demo-student/src/config/locale/zh-cn.ts 
export default {
  FindBook: 'Find a book',
};Use the $locale method for language translation, and support automatic type hints for language resources
import { $locale } from '../.metadata/index.ts';
@Web.get(':id', {
  description: $locale('FindBook'),
})3. Action options list 
| Name | Description | 
|---|---|
| description | API description | 
| summary | API summary | 
| httpCode | Response http code | 
| contentType | Response content type | 
| bodySchema | Response body schema | 
| bodySchemaWrapper | Response body wrapper | 
| exclude | Whether to exclude this API in Swagger/Openapi metadata, default is false | 
| tags | API tag grouping | 
| operationId | API operation ID, default is methodName | 
| headers | Define Request Headers | 
| setHeaders | Set Response Headers | 
Controller options 
1. Example 
You can pass more options when defining a Controller
@Controller('book', {
  exclude: false,
  tags: ['Book'],
})
class ControllerBook {}- exclude: Do not display all APIs of this Controller in Swagger/Openapi metadata
 - tags: API tag grouping
 
2. Controller options list 
| Name | Description | 
|---|---|
| exclude | Whether to exclude all APIs of this Controller in Swagger/Openapi metadata, default is false | 
| tags | API tag grouping | 
| actions | Define Actions options | 
| enable | Whether to enable Controller | 
| meta | Enable Controller based on conditions | 
3. App config configuration 
Controller options can be configured in App config
For example, by default, http://localhost:7102/swagger is only valid in test/dev environment. If you want to make Swagger accessible in the prod environment, you can configure it in App config
src/backend/config/config/config.prod.ts
// onions
config.onions = {
  controller: {
    'a-swagger:swagger': {
      meta: {
        mode: ['test', 'dev', 'prod'],
      },
    },
  },
};4. actions 
Provide actions options in Controller options, allowing us to configure the Action options in any Controller in App config
For example, we set the action options of findOne in the ControllerBook class in App config:
src/backend/config/config/config.dev.ts
// onions
config.onions = {
  controller: {
    'demo-student:book': {
      actions: { 
        findOne: { 
          description: 'Find a book!!!', 
        }, 
      }, 
    }, 
  },
};