{"id":19594861,"url":"https://github.com/glebbatykov/minerva_controller_generator","last_synced_at":"2025-07-09T18:06:09.814Z","repository":{"id":64918334,"uuid":"579330161","full_name":"GlebBatykov/minerva_controller_generator","owner":"GlebBatykov","description":"Minerva code generator for work with controllers. Controllers are needed to configure the server.","archived":false,"fork":false,"pushed_at":"2023-10-02T23:45:54.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-09T13:18:37.016Z","etag":null,"topics":["code-generation","controller","dart","minerva","rest","server"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GlebBatykov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2022-12-17T10:33:34.000Z","updated_at":"2023-01-04T10:53:59.000Z","dependencies_parsed_at":"2024-11-11T08:45:09.824Z","dependency_job_id":"7def5cb6-bc3c-4825-8422-2bb754264948","html_url":"https://github.com/GlebBatykov/minerva_controller_generator","commit_stats":{"total_commits":23,"total_committers":1,"mean_commits":23.0,"dds":0.0,"last_synced_commit":"2c35c7220da2240eb9cdba1504eb2a3e57cb3052"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlebBatykov%2Fminerva_controller_generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlebBatykov%2Fminerva_controller_generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlebBatykov%2Fminerva_controller_generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlebBatykov%2Fminerva_controller_generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GlebBatykov","download_url":"https://codeload.github.com/GlebBatykov/minerva_controller_generator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240867972,"owners_count":19870509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["code-generation","controller","dart","minerva","rest","server"],"created_at":"2024-11-11T08:45:02.441Z","updated_at":"2025-02-26T14:22:18.927Z","avatar_url":"https://github.com/GlebBatykov.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n[![pub package](https://img.shields.io/pub/v/minerva_controller_generator.svg?label=minerva_controller_generator\u0026color=blue)](https://pub.dev/packages/minerva_controller_generator)\n\n**Languages:**\n  \n[![English](https://img.shields.io/badge/Language-English-blue?style=?style=flat-square)](README.md)\n[![Russian](https://img.shields.io/badge/Language-Russian-blue?style=?style=flat-square)](README.ru.md)\n\n\u003c/div\u003e\n\n- [About package](#about-package)\n- [Using](#using)\n- [What is controller](#what-is-controller)\n- [Actions](#actions)\n- [Forming the endpoint path](#forming-the-endpoint-path)\n- [Action parameters](#action-parameters)\n- [Websocket endpoints](#websocket-endpoints)\n\n# About package\n\nThe package is designed to facilitate server configuration when using the `Minerva` framework.\n\nThere are two ways to configure endpoints in the `Minerva` framework:\n\n- using class derived from the `MinervaEndpointsBuilder` class, configure each endpoint individually;\n- using class derived from the `MinervaApisBuilder` class, configure the server using the `Api`. An `Api` is a somewhat unified endpoint with some common context within the `Api` class.\n\nBut all these methods forced me to write a lot of extra code. A more elegant way of configuring endpoints was needed, for example, as controllers in ASP.NET and other frameworks.\n\nSome other frameworks have implemented something similar using the library for reflection - dart:mirrors. However, such an option would not allow using AOT compilation.\n\nThis package implements controllers built on code generation. With it, you can configure your server using controllers, controller actions, data binding in controller action parameters, etc., and then the package generates an `Api` that builds endpoint routes, generates a code binding these action parameters to data from the request. You connect the generated `Api` in a class derived from the `MinervaEndpointsBuilder` class.\n\n# Using\n\nCreating the file `hello_controller.dart` with the controller `HelloController` and with one GET action `get`, which returns `Hello, world!`:\n\n```dart\nimport 'package:minerva/minerva.dart';\nimport 'package:minerva_controller_annotation/minerva_controller_annotation.dart';\n\npart 'hello_controller.g.dart';\n\nclass HelloController extends ControllerBase {\n  @Get()\n  String get() {\n    return 'Hello, world!';\n  }\n}\n```\n\nUsing this package we get the file `hello_controller.g.dart` with the following contents:\n\n```dart\npart of 'hello_controller.dart';\n\nclass HelloApi extends Api {\n  final ControllerBase _controller = HelloController();\n\n  @override\n  Future\u003cvoid\u003e initialize(ServerContext context) async {\n    await _controller.initialize(context);\n  }\n\n  @override\n  void build(Endpoints endpoints) {\n    endpoints.get('/hello', (context, request) async {\n      return (_controller as HelloController).get();\n    }, errorHandler: null, authOptions: null, filter: null);\n  }\n\n  @override\n  Future\u003cvoid\u003e dispose(ServerContext context) async {\n    await _controller.dispose(context);\n  }\n}\n```\n\nConnecting the generated `Api`:\n\n```dart\nclass ApisBuilder extends MinervaApisBuilder {\n  @override\n  List\u003cApi\u003e build() {\n    final apis = \u003cApi\u003e[];\n\n    apis.add(HelloApi());\n\n    return apis;\n  }\n```\n\n# What is controller\n\nThe controller is class derived from the `ControllerBase` class.\n\nIt contains the `initialize` and `dispose` methods, as well as the `Api`, but it does not contain the `build` method, since you configure endpoints using actions.\n\nExample of creating controller class:\n\n```dart\nclass HelloController extends ControllerBase {}\n```\n\n# Actions\n\nActions are controller methods marked with action annotation classes for this:\n\n- `Get` - for actions processing incoming `GET` requests;\n- `Post` - for actions processing incoming `POST` requests;\n- `Head` - for actions processing incoming `HEAD` requests;\n- `Options` - for actions processing incoming `OPTIONS` requests;\n- `Patch` - for actions processing incoming `PATCH` requests;\n- `Put` - for actions processing incoming `PUT` requests;\n- `Delete` - for actions processing incoming `DELETE` requests;\n- `Trace` - for actions processing incoming `TRACE` requests.\n\nAnnotations for actions contain the same parameters as endpoints in Minerva.\n\nIn the abstract, you can specify:\n\n- `authOptions` - authentication settings for the endpoint, for more information, see [here](https://github.com/GlebBatykov/minerva#authentication);\n- `filter` - filter for the endpoint, for more information, see [here](https://github.com/GlebBatykov/minerva#request-filter);\n- `ErrorHandler` - handler for errors that occur during the execution of the endpoint handler.\n\nExample of creating 'Hello, world!' controller with single `GET` action:\n\n```dart\nclass HelloController extends ControllerBase {\n  @Get()\n  String hello() {\n    return 'Hello, world!';\n  }\n}\n```\n\nAs result, in the example above, we will get the `GET` endpoint `/hello`. The rules for forming endpoint paths are described [here](#forming-the-endpoint-path).\n\nExample of creating controller with multiple actions:\n\n```dart\nclass UsersController extends ControllerBase {\n  @Post()\n  void add() {\n    /* execute some code */\n  }\n\n  @Get()\n  String get() {\n    return 'Some user';\n  }\n\n  @Patch()\n  void edit() {\n    /* execute some code */\n  }\n}\n```\n\nAs result, in the example above, we will get the following endpoints:\n\n- `POST` endpoint `/users/add`;\n- `GET` the endpoint `/users`;\n- `PATCH` the endpoint `/users/edit`.\n\nExample of creating an action that will be available only to users with the Admin role:\n\n```dart\nclass UsersController extends ControllerBase {\n  @Delete(authOptions: AuthOptions(jwt: JwtAuthOptions(roles: ['Admin'])))\n  void delete() {\n    /* execute some code */\n  }\n}\n```\n\nAs result, in the example above, we will get the `DELETE` endpoint `/users`, accessible only to users with the Admin role.\n\n# Forming the endpoint path\n\nEndpoint paths are formed based on the controller name, action name, path templates that are specified in annotations, as well as taking into account some specific rules.\n\nThe full path to the endpoint consists of the path to the controller, as well as the path to the action.\n\nBoth the controller and the action have templates of their path.\n\nIn the annotation for the controller `@Controller()`, you can specify the path parameter, which is a template for the controller path. In it, you can use the controller name by typing `{controller}` into it. By default, the controller path template is `/{controller}`. Controller name is the name of the controller class, without `Controller` at the end of the name, the name is reduced to lowercase.\n\nIn the annotations to actions, you can specify the path parameter, which is the template of the action path. In it, you can use the controller name by typing `{action}` into it. By default, the action path template is `/{action}`. The action name is the name of the action method reduced to lowercase.\n\nIn the annotations to the endpoints of the websockets `@WebSocketEndpoint()`, you can specify the path parameter, which is a template for the path of the endpoint of the websocket connections. In it, you can use the method name of the websocket connection handler by typing `{endpoint}` into it. By default, the path template of the websocket endpoint is `/{endpoint}`. The name of the endpoint of the websocket is the name of the handler method reduced to lowercase.\n\nThere are also some rules for forming path:\n\nIf the action name ends with the name of the HTTP method whose requests it processes, then the name is truncated to the name of the HTTP method.\n\nExample: the name of the `GET` action was `userGet`, it became `user`.\n\nIf the name of the action completely coincides with the name of the HTTP method whose requests it processes, as well as the template of the action name is default, then when forming the endpoint path, only the path to the controller is taken into account.\n\nExample: we have a UsersController controller, it has a `GET` action named get. The controller and the action have default path templates. The path to the `GET` endpoint will be `/users`.\n\nIf the name of the websocket endpoint handler ends with the word `Endpoint`, then it is truncated to it.\n\nExample: it was `webSocketEndpoint`, it became `WebSocket`.\n\nExample of specifying your own controller path and action template:\n\n```dart\n@Controller(path: '/api/{controller}')\nclass TestController extends ControllerBase {\n  @Get(path: '/some/test')\n  void test() {}\n}\n```\n\n# Action parameters\n\nEndpoint handlers in Minerva always receive two parameters, this is an instance of the `ServerContext` class, as well as an instance of the `MinervaRequest` class for the context of the current request.\n\nWhen configuring endpoints, it was not always convenient to prescribe these parameters, because they may not always be useful to us. And it was also not convenient to extract data from the query, writing the same code for this every time.\n\nActions in controllers may not have any parameters, but they may have them.\n\nYou can, if necessary, specify in the request parameters parameters with the types `ServerContext`, `MinervaRequest` and get them.\n\nExample of getting `ServerContext` and `MinervaRequest` instances in actions:\n\n```dart\nclass TestController extends ControllerBase {\n  @Get()\n  void first() {}\n\n  @Get()\n  void second(ServerContext context) {}\n\n  @Get()\n  void third(ServerContext context, MinervaRequest request) {}\n}\n```\n\nIn addition to receiving instances of `ServerContext` and `MinervaRequest`, you can associate action parameters with request data, and you can also receive authentication data. Authentication data is taken from `MinervaRequest`, you can get instances of `AuthContext`, `JwtAuthContext`, `CookieAuthContext` from there.\n\nYou can associate parameters with query data using parameter annotations:\n\n- `FromQuery` - connection with the data of the query parameters;\n- `FromRoute` - connection with the data of the request path parameters;\n- `FromBody` - connection with the data of the request body, it is assumed that the request body contains data of the type `application/json`;\n- `FromForm` - connection with the data of the request body, it is assumed that the request body contains data of the type `multipart/form-data`.\n\nWhen linking data, the parameter name is taken as the parameter name/data field. You can also set another name for binding using the name parameter of the data binding annotation classes (`FromQuery`, `FormRoute`, `FromBody`, `FormForm`).\n\nExample of getting data from query parameters:\n\n```dart\nclass UsersController extends ControllerBase {\n  @Get()\n  dynamic get(@FromQuery() int id) {\n    /* execute some code */\n  }\n}\n```\n\nWhen getting data from a query parameter, you can specify the parameter types `String`, `bool`, `int`, `double`, `num`. Types can be nullable.\n\nExample of getting data from request path parameters:\n\n```dart\nclass UsersController extends ControllerBase {\n  @Get(path: '/:id')\n  dynamic get(@FromRoute() int id) {\n    /* execute some code */\n  }\n}\n```\n\nWhen getting data from the request path parameters, you can specify the parameter types `num`, `int`, `double`, `bool`, `String`. Types can be nullable.\n\nWhen receiving data from the JSON request body, using the annotation `@FromBody`, you can specify the types of parameters `String`, `int`, `double`, `num`. Also, you can specify as a type an arbitrary type of your data model, which contains the `fromJson` constructor. Thus, you can immediately get deserialized data into the model in the controller action in the parameter. You can also specify `List` and `Map` as parameters, and they can have any degree of nesting.\n\nExample of getting data from the JSON request body:\n\n```dart\nclass UsersController extends ControllerBase {\n  @Post()\n  void add(@FromBody() String name, @FromBody() int age) {\n    /* execute some code */\n  }\n}\n```\n\nExample of getting data from JSON request body, deserializing it into model:\n\n```dart\nclass User {\n  final String name;\n\n  final int age;\n\n  User.fromJson(Map\u003cString, dynamic\u003e json)\n      : name = json['name'],\n        age = json['age'];\n}\n\nclass UsersController extends ControllerBase {\n  @Post(path: '/add/one')\n  void add(@FromBody() User user) {\n    /* execute some code */\n  }\n\n  @Post(path: '/add/many')\n  void addMany(@FromBody() List\u003cUser\u003e users) {\n    /* execute some code */\n  }\n}\n```\n\nWhen receiving data from the request body, which is form, using the `@FromForm` annotation, you can specify the types of parameters `FormDataString`, `FormDataFile`. These types correspond to form fields that contain string values and files.\n\nExample of getting data from request form:\n\n```dart\nclass FilesController extends ControllerBase {\n  @Post()\n  void add(@FromForm() FormDataString name, @FromForm() FormDataFile file) {\n    /* execute some code */\n  }\n}\n```\n\n# Websocket endpoints\n\nIn controllers, in addition to actions for processing incoming `HTTP` requests, you can also set endpoints for processing websocket connections.\n\nThe method that should serve as handler for the endpoint of websockets is marked with the annotation `@WebSocketEndpoint`.\n\nBinding parameters to query data does not work in this case. In the parameters of the method of the websocket connection handler, you must specify parameter of the type `WebSocket` to get an instance of the websocket with which you can work. You can also specify parameter of the type `ServerContext` in the parameters to get an instance of the server context.\n\nExample of creating an endpoint for processing websocket connections:\n\n```dart\nclass HelloController extends ControllerBase {\n  @WebSocketEndpoint()\n  Future\u003cvoid\u003e hello(WebSocket socket) async {\n    socket.add('Hello, world!');\n\n    await socket.close();\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglebbatykov%2Fminerva_controller_generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglebbatykov%2Fminerva_controller_generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglebbatykov%2Fminerva_controller_generator/lists"}