{"id":18933211,"url":"https://github.com/gordey4doronin/fully-typed-example","last_synced_at":"2026-03-05T02:32:19.526Z","repository":{"id":149756098,"uuid":"56487891","full_name":"gordey4doronin/fully-typed-example","owner":"gordey4doronin","description":"The project example for the article about API documentation and typings generation with TypeScript, Swashbuckle and AutoRest.","archived":false,"fork":false,"pushed_at":"2017-04-23T08:48:32.000Z","size":729,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-15T19:48:10.325Z","etag":null,"topics":["autorest","static-typing","swagger","swashbuckle","typescript"],"latest_commit_sha":null,"homepage":null,"language":"C#","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/gordey4doronin.png","metadata":{"files":{"readme":"README.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}},"created_at":"2016-04-18T07:50:26.000Z","updated_at":"2025-02-14T10:34:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"94549028-57c9-4a05-a670-702588f54211","html_url":"https://github.com/gordey4doronin/fully-typed-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gordey4doronin/fully-typed-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gordey4doronin%2Ffully-typed-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gordey4doronin%2Ffully-typed-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gordey4doronin%2Ffully-typed-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gordey4doronin%2Ffully-typed-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gordey4doronin","download_url":"https://codeload.github.com/gordey4doronin/fully-typed-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gordey4doronin%2Ffully-typed-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30107212,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T01:39:18.192Z","status":"online","status_checked_at":"2026-03-05T02:00:06.710Z","response_time":93,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["autorest","static-typing","swagger","swashbuckle","typescript"],"created_at":"2024-11-08T11:52:11.690Z","updated_at":"2026-03-05T02:32:19.507Z","avatar_url":"https://github.com/gordey4doronin.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"en-US :us: | [ru-RU :ru:](https://github.com/gordey4doronin/fully-typed-example/blob/master/README.ru-RU.md)\n# On the way to full typing with TypeScript, Swashbuckle, and AutoRest\n\n## Introduction\n\nThe article is devoted to the question of implementing exchange of typed messages between Back-End based on ASP.NET Web API and Front-End created with the use of TypeScript.\nIt is important for the teams working on big projects, especially for the distributed ones.\nFor example, when Back-End and Front-End developers work from different places and time zones often lacking a chance to communicate and discuss any issue.\nTracking changes in this case can be rigorous and causing elusive errors sometimes.\n\nOne of the main problems the author (who came to Front-End from WPF and Silverlight) faced was the absence of static typing.\nQuite often instead of adding “2” and “2” he added “2” and “Callback function returning 2”, or passed DOM object instead of its jQuery wrapper.\nOf course, occurrence of such static code analyzers as JSLint served as a solution for this problem.\nHowever, TypeScript was a real breakthrough, especially for the teamwork.\n\n![TypeScript + Swagger logo](images/typescript-swagger.png)\n\n## Problem Key Part\n\nTypeScript is a programming language that allows reaching static typing, even though some people call it “Illusion” ( https://habrahabr.ru/post/258957/, https://habrahabr.ru/post/272055/ ).\nIt is interesting that critics describe an interaction with a Back-End as a typical not type-safe scenario.\nActually, the problem is that when writing Front-End application using JavaScript before and TypeScript now, we do not have the same tools to work with metadata and auto-generate client code, which we had in WCF for example.\n\n## Metadata\nAs for WPF+WCF experience, everything works well there. Even though data always tranfsers as not typed, while sending it is typed until the very end. Only right before it is sent to the other side, it is serialized into string or binary stream.\nAfter that, some client on the other side makes it typed.\nIn order not to write such a client by hand trying to fix numerous errors, metadata is used.\nIn the .NET world, 90% of situations do not require any additional work for either its generation or processing.\nYou just write your service adding a corresponding endpoint and receive auto-generated metadata.\nThen in one click, you generate the client and, as a result, you get the exchange of typed messages.\n\nWeb API comes to change WCF for developing Single Page Application using JavaScript/TypeScript.\nThe absence of ways to generate metadata for Web API from the box (we cannot regard help-pages as metadata) was quite surprising for the first time.\nSeems, the thing is that the main data recipient from Web API was JavaScript code, in which typing does not play any sense.\nHowever, now we have TypeScript instead of JavaScript, which means operating typed data takes its role again.\n\nNow one of the most popular metadata format is OpenAPI/Swagger. No wonder that this format gives new opportunities to generate metadata and documentation.\n\nThe article will demonstrate the process of organization of typed interoperability through these steps:\n\n1. Add and set up Swashbuckle library\n2. Generate documentation/metadata\n3. Check it is comfortable to store generated file in the version control system\n4. Add AutoRest\n5. Generate client models\n6. Try them in work\n\n## Swashbuckle\nhttps://github.com/domaindrivendev/Swashbuckle\n\nFirst, we need to generate metadata. So, let us assume that we have Web API with the controller responsible for the work with the employees.\n\n```cs\n/// \u003csummary\u003e\n/// Gets all employees.\n/// \u003c/summary\u003e\n/// \u003cremarks\u003e\n/// Gets the list of all employees.\n/// \u003c/remarks\u003e\n/// \u003creturns\u003e\n/// The list of employees.\n/// \u003c/returns\u003e\n[Route(\"api/employees\")]\n[HttpGet]\npublic Employee[] GetEmployees()\n{\n    return new[]\n        {\n            new Employee { Id = 1, Name = \"John Doe\" },\n            new Employee { Id = 2, Name = \"Jane Doe\" }\n        };\n}\n```\n\nAs you see, the array of the typed objects of Employee type is returned.\nRunning the project, we can make a query of the employee list: http://localhost:1234/api/employees\n\nNow it is turn to add Swashbuckle library.\nIn NuGet there are two packages: Swashbuckle.Core and Swashbuckle.\nThe difference between them is that the first is the core with the code making all magic, and the second is an addition, which installs bootstrapper to configure that core.\n\nIt is mentioned in the documentation: https://github.com/domaindrivendev/Swashbuckle#iis-hosted\n\nWe prefer to install the Core and write the configuration code ourselves, for it will be more comfortable to re-use it.\n\nLet us install it\n\n```\nPM\u003e Install-Package Swashbuckle.Core\n```\n\nthen register with the help of WebActivatorEx\n\n```cs\n[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(FullyTypedExample.WebApi.SwaggerConfig), \"RegisterGlobal\")]\n```\n\nand write the configuration code\n\n```cs\n/// \u003csummary\u003e\n/// Configures Swagger.\n/// \u003c/summary\u003e\n/// \u003cparam name=\"config\"\u003e\n/// The Swagger configuration.\n/// \u003c/param\u003e\npublic static void ConfigureSwagger(SwaggerDocsConfig config)\n{\n    config.SingleApiVersion(\"v1\", \"FullyTypedExample.WebApi\");\n    config.IncludeXmlComments(GetXmlCommentsPathForControllers());\n    config.IncludeXmlComments(GetXmlCommentsPathForModels());\n    config.GroupActionsBy(apiDescription =\u003e apiDescription.ActionDescriptor.ControllerDescriptor.ControllerName);\n    config.OrderActionGroupsBy(Comparer\u003cstring\u003e.Default);\n    config.PrettyPrint();\n}\n```\n\nIt is quite simple: first, we set the version and the API header.\nThen we turn on xml-doc for the controllers and models.\nAfter that we configure the order and action grouping inside the swagger-document.\nWe should also mention PrettyPrint option turning on JSON formatting for swagger-document.\nThis option is required for storing documentation in the version control system and easily review it using any of the diff viewer.\n\nNow we can launch the project and see Swagger user interface:\nhttp://localhost:1234/swagger\n\n![Swagger UI](images/swagger-start.png)\n\nIt is also possible to see the swagger-document in JSON format.\nhttp://localhost:1234/swagger/docs/v1\n\nNow we need to put the generated documentation into the version control system.\nAs soon as Swashbuckle uses Microsoft IApiExplorer under the hood, it is required to run Web API first to generate swagger file (find more on this: https://github.com/domaindrivendev/Swashbuckle/issues/559).\nTherefore, every time when you want to generate the documentation you have to run Web API and copy swagger/docs into the file manually.\nOf course, something more automated is preferred.\n\nWe solved this issue with the help of running Web API as a self-hosted application, sending request to swagger endpoint and writing the response into the file.\nAt this point, we needed to reuse the Swashbuckle configuration code. This is how it looks like:\n\n```cs\n/// \u003csummary\u003e\n/// Generate Swagger JSON document.\n/// \u003c/summary\u003e\n/// \u003cparam name=\"filePath\"\u003e\n/// The file path where to write the generated document.\n/// \u003c/param\u003e\nprivate static void GenerateSwaggerJson(string filePath)\n{\n    // Start OWIN host\n    using (TestServer server = TestServer.Create\u003cWebApiHostStartup\u003e())\n    {\n        HttpResponseMessage response = server.CreateRequest(\"/swagger/docs/v1\").GetAsync().Result;\n\n        string result = response.Content.ReadAsStringAsync().Result;\n        string path = Path.GetFullPath(filePath);\n\n        File.WriteAllText(path, result);\n    }\n}\n```\n\nLet’s launch it all: \n\n```bash\nnuget.exe restore \"..\\FullyTypedExample.sln\"\n\"C:\\Program Files (x86)\\MSBuild\\12.0\\bin\\MSBuild.exe\" \"..\\FullyTypedExample.WebApi.SelfHosted\\FullyTypedExample.WebApi.SelfHosted.proj\" /v:minimal\n\"..\\FullyTypedExample.WebApi.SelfHosted\\bin\\Debug\\FullyTypedExample.WebApi.SelfHosted.exe\" --swagger \"swagger.json\"\n```\n\nAs a result, we got the swagger-document in a JSON file and put it into the version control system.\nNow the Front-End developers from our distributed team can easily track changes in the endpoints.\nLet us see how it works.\n\nFor example, we added a new action to get the employee by the ID.\n\n```cs\n/// \u003csummary\u003e\n/// Gets employee by id.\n/// \u003c/summary\u003e\n/// \u003cparam name=\"employeeId\"\u003e\n/// The employee id.\n/// \u003c/param\u003e\n/// \u003cremarks\u003e\n/// Gets the employee by specified id.\n/// \u003c/remarks\u003e\n/// \u003creturns\u003e\n/// The \u003csee cref=\"Employee\"/\u003e.\n/// \u003c/returns\u003e\n[Route(\"api/employees/{employeeId:int}\")]\npublic Employee GetEmployeeById(int employeeId)\n{\n    return this.GetEmployees().SingleOrDefault(x =\u003e x.Id == employeeId);\n}\n```\n\nAnd then re-generated swagger.json. This is what changed:\n\n![Swagger docuemnt diff](images/swagger-diff.png)\n\nAs you can see, a new documentation, which is possible to view with any of the diff viewer, appeared for this action. Owing to PrettyPrint option, it is formatted and easy to read.\n\n## AutoRest\nhttps://github.com/Azure/autorest\n\nSo, the first task has been completed: we have metadata now.\nHow should we generate client code (i.e., the data types received from the server)?\n\nIt is worth to mention that it is possible to generate code for requesting Web API, although it is a bit harder and requires more efforts for code-generators configuration or writing your own ones.\nAlso, much depends on which libraries (jQuery, SuperAgent, or experimental Fetch API https://developer.mozilla.org/en/docs/Web/API/Fetch_API) and approaches (Promises, Rx, etc.) you use in your client code.\n\nThere are some options for code-generation:\n\n1. **Swagger Code Generator** https://github.com/swagger-api/swagger-codegen\nThe official tool from the Swagger team written in Java and requiring the corresponding infrastructure.\nIt also can be started in Docker. However, it lacks JavaScript and especially TypeScript generation. Though, if you need to generate code, for instance in Java, it will be a good choice. As for us, we obviously could not use it.\n\n2. **Swagger JS library** https://github.com/swagger-api/swagger-js\nOne more official tool from the Swagger team written in JS and generating JS code. It is installed through the nmp or bower. Infrastructure is quite suitable for us, but unfortunately it lacks type generation.\n\n3. **Swagger to JS \u0026 Typescript Codegen** https://github.com/wcandillon/swagger-js-codegen\nThe project was published later after we started developing this approach. Probably it will become the most suitable solution in the future. \n\n4. **Write your own ~~bicycle~~ code-generator.**\nWhy not? Nevertheless, initially we decided to use AutoRest. If it fails, we will write our own with blackjack and … You know what we mean.\n\n5. **AutoRest** https://github.com/Azure/autorest\nFinally, it is turn of AutoRest from the Azure Microsoft team. The most recent version is 0.15.0. It is not quite clear whether it is the ready-to-use release or not, but we do not see any “Pre” note, like in previous ones.\nHere it went simple, we installed and immediately generated *.d.ts files, which we actually needed.\n\nSo, now let’s pass to the final path with the help of this tool.\n\nAdd AutoRest through NuGet:\n\n```\nPM\u003e Install-Package AutoRest\n```\n\nThe package is not installed into the particular project, the reference to it is added for the whole solution.\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cpackages\u003e\n  \u003cpackage id=\"AutoRest\" version=\"0.15.0\" /\u003e\n\u003c/packages\u003e\n```\n\nThe package has the console application AutoRest.exe that does the generation. To start we use the following script: \n\n```bash\nnuget.exe restore \"..\\FullyTypedExample.sln\"\n\"..\\packages\\AutoRest.0.15.0\\tools\\AutoRest.exe\" -Input \"swagger.json\" -CodeGenerator NodeJS\nmove \"Generated\\models\\index.d.ts\" \"..\\FullyTypedExample.HtmlApp\\models.d.ts\"\n```\n\nWe input the generated swagger.json, and as an output, we get models\\index.d.ts file with models. Then we copy it to the client project.\n\nNow we have the following model description in TypeScript:\n\n```typescript\n/**\n * @class\n * Initializes a new instance of the Employee class.\n * @constructor\n * Represents the employee.\n * @member {number} id Gets or sets the employee identifier.\n * \n * @member {string} name Gets or sets the employee name.\n * \n */\nexport interface Employee {\n    id: number;\n    name: string;\n}\n```\n\nLet’s try it in action:\n\n```typescript\npublic makeRequest() {\n    this.repository.getEmployees()\n        .then((employees) =\u003e {\n            // Generate html using tempalte string\n            this.table.innerHTML = employees.reduce\u003cstring\u003e((acc, x) =\u003e {\n                    return `${acc}\u003ctr\u003e\u003ctd\u003e${x.id}\u003c/td\u003e\u003ctd\u003e${x.name}\u003c/td\u003e\u003c/tr\u003e`;\n                }, '');\n        });\n}\n```\n\nHere we use the id and name model fields.\nWe intentionally skipped the server request implementation, as we mentioned it depends on chosen libraries and approaches.\n\nIf we try to access a non-existing age field, our TS code will not be complied.\nIf the API field which we have referred before disappears, the code will not be compiled again.\nIf some new fields are added, we will see it immediately using the already known diff tool. Moreover, we automatically get JSDoc documentation based on metadata. Well, all the benefits of static typing at work.\n\n## ResponseType\n\nWhat is interesting, there is a possibility to override returned type, if it is necessary for the documentation.\nFor example, it can be useful for the legacy-code working with non-typed DataSets, or if you return IHttpActionResult from the controllers.\nWe can mark methods by ResponseType attribute, not touching the implementation, and develop special types:\n\n```cs\n/// \u003csummary\u003e\n/// Gets all departments.\n/// \u003c/summary\u003e\n/// \u003cremarks\u003e\n/// Gets the list of all departments.\n/// \u003c/remarks\u003e\n/// \u003creturns\u003e\n/// The list of departments.\n/// \u003c/returns\u003e\n[Route(\"api/departments\")]\n[HttpGet]\n[ResponseType(typeof(DepartmentsResponse))]\npublic DataSet GetDepartments()\n{\n    var dataTable = new DataTable(\"Departments\");\n\n    dataTable.Columns.Add(\"Id\", typeof(int));\n    dataTable.Columns.Add(\"Name\", typeof(string));\n\n    dataTable.Rows.Add(1, \"IT\");\n    dataTable.Rows.Add(2, \"Sales\");\n\n    var dataSet = new DataSet();\n    dataSet.Tables.Add(dataTable);\n\n    return dataSet;\n}\n```\n\nin order to get the typed models on the client side\n\n```typescript\n/**\n * @class\n * Initializes a new instance of the Department class.\n * @constructor\n * Represents the department.\n * @member {number} id Gets or sets the department identifier.\n * \n * @member {string} name Gets or sets the department name.\n * \n */\nexport interface Department {\n    id: number;\n    name: string;\n}\n```\n\n## Problems\n\nFirst, models.d.ts file grows with the time.\nFor now, we have not tried to separate it into a few sub-files, but, surely, it will be necessary to do.\n\nSecond, there can occur the problem with the incorrect field names generation while using the non-standard notation. For example, if snake_case (i.e., underscore notation) is used.\nLAST_NAME field from C# code is generated to Swagger as lasT_NAME, and to TypeScrpt as lasTNAME.\n\n```cs\n/// \u003csummary\u003e\n/// Gets or sets the last name.\n/// \u003c/summary\u003e\n[Required]\n// ReSharper disable once InconsistentNaming\npublic string LAST_NAME { get; set; }\n```\n\n```json\n\"lasT_NAME\": {\n  \"description\": \"Gets or sets the last name.\",\n  \"type\": \"string\"\n}\n```\n\n```typescript\nexport interface Employee {\n    id: number;\n    name: string;\n    firstName: string;\n    lasTNAME: string;\n}\n```\n\nBy the way, most of the minor issues are easily solved with the help of the configuration and are not worth to mention.\n\n## Conclusion\n\nThis approach let us organize the exchange of the typed messages.\nMoreover, it provided typing of the client models, lowered the possible inconsistence of client and server code, and improved the source changes tracking in API and models.\nOne of the nice benefits of it is the comfortable manual API testing with the built in REST-client and possibility to generate payload on-the-fly using model schema.\nUsing this approach also helped to improve the interaction between Back-End and Front-End developers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgordey4doronin%2Ffully-typed-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgordey4doronin%2Ffully-typed-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgordey4doronin%2Ffully-typed-example/lists"}