# Generate TypeScript Client from dotnet API https://blog.logrocket.com/generate-typescript-csharp-clients-nswag-api/ ## Introdution To generate a TypeScript client or a C# client from a dotnet API using NSwag, we will build five projects to demo. 1. The `api` project will implement an endpoint that provides a `User.get()` restful API and a `swagger` HTML page. 2. The `api_generator` project will generate the TypeScript/C# client code. 3. The `client` project will create a `Vite-React` client project which will demo `multiple fetching function`. 4. The `client-console` project will create a dotnet console app which using `C# client code` generated by `api-generator` to fetch endpoint. the final folder structure will be like : ``` . ├── README.md ├── api │ ├── Controllers │ ├── Models │ ├── Program.cs │ ├── Properties │ ├── WeatherForecast.cs │ ├── api.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bin │ └── obj ├── api_generator │ ├── Program.cs │ ├── api_generator.csproj │ ├── api_generator.sln │ ├── bin │ └── obj ├── client │ ├── dist │ ├── index.html │ ├── node_modules │ ├── package.json │ ├── public │ ├── src │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite.config.ts │ └── yarn.lock ├── client-console │ ├── Program.cs │ ├── bin │ ├── client-console.csproj │ ├── clients.cs │ └── obj ├── node-ts │ ├── node_modules │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── tsconfig.json │ └── yarn.lock ├── package.json └── yarn.lock ``` ## Step 1 : Setup folder structure ```bash mkdir api mkdir api_generator mkdir client mkdir client-console mkdir node-ts ``` ## Step 2 : Create API end point project ```bash cd api dotnet new webapi -o . dotnet remove package Swashbuckle.AspNetCore dotnet add package NSwag.AspNetCore dotnet add package Microsoft.AspNetCore.Cors dotnet new gitignore ``` Then create a folder witch named `Models` and create `UserModle.cs` in this folder ```C# namespace api.Models; public class UserModel { public string Name { get; set; } public string? Email { get; set; } } ``` Let create `UserController` in Controllers folder you can see the source code that has a `[HttpGet(Name = "GetUser")]` function which providing a api to get the UserModels. ```C# using System.Net.NetworkInformation; using System.Runtime.InteropServices.ComTypes; using System.IO; using api.Models; using Microsoft.AspNetCore.Mvc; namespace api.Controllers; [ApiController] [Route("[controller]")] public class UserController : ControllerBase { private List<UserModel> userList = new List<UserModel>(){ new UserModel(){ Name = "Adem", Email= "adem@pic.net" }, new UserModel(){ Name="Ted", Email = "ted@pic.net" } } ; private readonly ILogger<UserController> _logger; public UserController(ILogger<UserController> logger) { _logger = logger; } [HttpGet(Name = "GetUser")] public IEnumerable<UserModel> Get() { return userList; // yield return new UserModel() { Name = "Adem" }; } } ``` and the most important things is changing some code in `Program.cs` 1. change `builder.Services.AddSwaggerGen();` to `builder.Services.AddSwaggerDocument();` 2. change ` app.UseSwagger();` and `app.UseSwaggerUi();` to `app.UseOpenApi();`,`app.UseSwaggerUi3();` ```C# // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { // app.UseSwagger(); // app.UseSwaggerUi(); app.UseDeveloperExceptionPage(); // Register the Swagger generator and the Swagger UI middlewares app.UseOpenApi(); app.UseSwaggerUi3(); } ``` 3. Add Cors ```C# const string ALLOW_DEVELOPMENT_CORS_ORIGINS_POLICY = "AllowDevelopmentSpecificOrigins"; const string LOCAL_DEVELOPMENT_URL = "http://127.0.0.1:5173"; //your client url builder.Services.AddCors(options => { options.AddPolicy(name: ALLOW_DEVELOPMENT_CORS_ORIGINS_POLICY, policy => { policy.WithOrigins(LOCAL_DEVELOPMENT_URL) .AllowAnyHeader() .AllowAnyMethod() ; }); }); //..... app.UseCors(ALLOW_DEVELOPMENT_CORS_ORIGINS_POLICY); ``` Let's run the project ```bash ASPNETCORE_URLS=http://*:5001 ASPNETCORE_ENVIRONMENT=Development dotnet watch --project . run --no-launch-profile ``` and you can open `http://localhost:5001/swagger/index.html` in the browser which show swagger page. Do not stop this project, we will use that url in api-generator. ## Step 3 : Create API-GENERATOR project move to the root folder and jump into api_generator folder ```bash cd .. cd api_generator dotnet new console -o . dotnet add package NSwag.CodeGeneration.CSharp dotnet add package NSwag.CodeGeneration.TypeScript dotnet add package NSwag.Core dotnet new gitignore ``` ## Step 4 : Create Client Vite-React using Vite to create react client project https://vitejs.dev/guide/ ``` cd client yarn create vite . --template react-ts ``` ## Step 5 : Client dotnet console App ``` mkdir client-console cd client-console dotnet new console -o . dotnet add package Newtonsoft.Json dotnet new gitignore ``` ## Step 6 : Add `package.json` and add some `script` in root folder ## Generator Project ``` mkdir api_generator cd api_generator dotnet new console -o . dotnet add package NSwag.CodeGeneration.CSharp dotnet add package NSwag.CodeGeneration.TypeScript dotnet add package NSwag.Core ``` change `Program.cs` ``` // See https://aka.ms/new-console-template for more information using System; using System.IO; using System.Threading.Tasks; using NJsonSchema.CodeGeneration.TypeScript; using NSwag; using NSwag.CodeGeneration.CSharp; using NSwag.CodeGeneration.TypeScript; if (args.Length != 3) throw new ArgumentException("Expecting 4 arguments: URL, generatePath, language"); var url = args[0]; var generatePath = args[1]; var language = args[2]; if (language != "TypeScript" && language != "CSharp") throw new AbandonedMutexException("Invalid language parameter; valid values are TypeScript and CSharp"); var document = await OpenApiDocument.FromUrlAsync(url); Console.WriteLine($"Generating {generatePath}..."); await System.IO.File.WriteAllTextAsync(generatePath, language switch { "TypeScript" => GenerateTypeScriptClient(document), "CSharp" => GenerateCSharpClient(document), _ => throw new AbandonedMutexException("Invalid language parameter; valid values are TypeScript and CSharp") }); static string GenerateTypeScriptClient(OpenApiDocument document) { var settings = new TypeScriptClientGeneratorSettings { TypeScriptGeneratorSettings = { TypeStyle = TypeScriptTypeStyle.Interface, TypeScriptVersion = 3.5M, DateTimeType = TypeScriptDateTimeType.String } }; var generator = new TypeScriptClientGenerator(document, settings); return generator.GenerateFile(); } static string GenerateCSharpClient(OpenApiDocument document) { var settings = new CSharpClientGeneratorSettings { UseBaseUrl = false }; var generator = new CSharpClientGenerator(document, settings); return generator.GenerateFile(); } ``` run console app - Generate TypeScript: ``` dotnet run --project . http://localhost:5001/swagger/v1/swagger.json ../client/clients.ts TypeScript ``` - Generate CShrap ``` dotnet run --project . http://localhost:5001/swagger/v1/swagger.json ../client-console/clients.cs CSharp ``` * Note: if got some errors `error CS0103: The name 'TypeScriptTypeStyle' does not exist in the current context `, please make sure you are using `NJsonSchema.CodeGeneration.TypeScript` in Program.cs ### Install Global Package ``` npm install -g npm-run-all ```