Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Typescript

OpenApiClientGen to Generate Strongly Typed Client API Codes Based on Open API Definitions

5.00/5 (1 vote)
3 Jul 2020CPOL4 min read 11K  
Generate Strongly Typed Client API Codes Based on Swagger / Open API Definitions in C# on .NET Frameworks and .NET Core, and in TypeScript for Angular 5+, Aurelia, jQuery, AXIOS and Fetch API

Introduction

OpenAPI Client Generators is a .NET Core command line program to generate strongly typed client API codes in C# on .NET Frameworks and .NET Core, and in TypeScript for Angular 5+, Aurelia, jQuery, AXIOS and Fetch API.

It is assumed that you already have decent knowledge and experience in Swagger/Open API specifications and respective client side application development.

OpenApiClientGen is an alternative solution besides the following tools typically used by .NET application developers:

They are decent tools, covering wider spectrums than OpenApiClientGen. And this article is intent to outline some differences for you to choose the right tool for the right contexts.

Background

The development of OpenApiClientGen is based on some core components of WebApiClientGen, and sharing the same principle design. Thus, the generated client codes share the same characteristics.

Using the code

How to Generate

When running Fonlow.OpenApiClientGen.exe without parameter, you will see the following hints:

BAT
Parameter 1: Open API YAML/JSON definition file
Parameter 2: Settings file in JSON format.
Example: 
  Fonlow.OpenApiClientGen.exe my.yaml
  Fonlow.OpenApiClientGen.exe my.yaml myproj.json
  Fonlow.OpenApiClientGen.exe my.yaml ..\myproj.json</code>

A typical CodeGen JSON file is like this "DemoCodeGen.json":

JavaScript
{
	"ClientNamespace": "My.Pet.Client",
	"ClientLibraryProjectFolderName": "./Tests/DemoClientApi",
	"ContainerClassName": "PetClient",
	"ClientLibraryFileName": "PetAuto.cs",
	"ActionNameStrategy": 4,
	"UseEnsureSuccessStatusCodeEx": true,
	"DecorateDataModelWithDataContract": true,
	"DataContractNamespace": "http://pet.domain/2020/03",
	"DataAnnotationsEnabled": true,
	"DataAnnotationsToComments": true,
	"HandleHttpRequestHeaders": true,

	"Plugins": [
		{
			"AssemblyName": "Fonlow.OpenApiClientGen.NG2",
			"TargetDir": "./ng2/src/clientapi",
			"TSFile": "ClientApiAuto.ts",
			"AsModule": true,
			"ContentType": "application/json;charset=UTF-8"
		}
	]

}

Example of Generated C# Codes Based on pet.yaml

C#
    /// <summary>
    /// A representation of a cat
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Cat : Pet
    {
        
        /// <summary>
        /// The measured skill for hunting
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="huntingSkill")]
        public CatHuntingSkill HuntingSkill { get; set; } = CatHuntingSkill.lazy;
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum CatHuntingSkill
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        clueless = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        lazy = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        adventurous = 2,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        aggressive = 3,
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Category
    {
        
        /// <summary>
        /// Category ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Category name
        /// Min length: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="name")]
        [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength=1)]
        public string Name { get; set; }
        
        /// <summary>
        /// Test Sub Category
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="sub")]
        public CategorySub Sub { get; set; }
    }
    
    public class CategorySub
    {
        
        /// <summary>
        /// Dumb Property
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="prop1")]
        public string Prop1 { get; set; }
    }
    
    /// <summary>
    /// A representation of a dog
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Dog : Pet
    {
        
        /// <summary>
        /// The size of the pack the dog is from
        /// Minimum: 1
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="packSize")]
        [System.ComponentModel.DataAnnotations.Range(1, System.Int32.MaxValue)]
        public int PackSize { get; set; } = 1;
    }
    
    /// <summary>
    /// A representation of a honey bee
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class HoneyBee : Pet
    {
        
        /// <summary>
        /// Average amount of honey produced per day in ounces
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="honeyPerDay")]
        public float HoneyPerDay { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Order
    {
        
        /// <summary>
        /// Order ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Pet ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="petId")]
        public System.Nullable<System.Int64> PetId { get; set; }
        
        /// <summary>
        /// Minimum: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="quantity")]
        [System.ComponentModel.DataAnnotations.Range(1, System.Int32.MaxValue)]
        public System.Nullable<System.Int32> Quantity { get; set; }
        
        /// <summary>
        /// Estimated ship date
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="shipDate")]
        public System.Nullable<System.DateTimeOffset> ShipDate { get; set; }
        
        /// <summary>
        /// Order Status
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="status")]
        public OrderStatus Status { get; set; }
        
        /// <summary>
        /// Indicates whenever order was completed or not
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="complete")]
        public System.Nullable<System.Boolean> Complete { get; set; }
        
        /// <summary>
        /// Unique Request Id
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="requestId")]
        public string RequestId { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum OrderStatus
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        placed = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        approved = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        delivered = 2,
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Pet
    {
        
        /// <summary>
        /// Pet ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Categories this pet belongs to
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="category")]
        public Category Category { get; set; }
        
        /// <summary>
        /// The name given to a pet
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="name")]
        public string Name { get; set; }
        
        /// <summary>
        /// The list of URL to a cute photos featuring pet
        /// Maximum items: 20
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="photoUrls")]
        [System.ComponentModel.DataAnnotations.MaxLength(20)]
        public string[] PhotoUrls { get; set; }
        
        [System.Runtime.Serialization.DataMember(Name="friend")]
        public Pet Friend { get; set; }
        
        /// <summary>
        /// Tags attached to the pet
        /// Minimum items: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="tags")]
        [System.ComponentModel.DataAnnotations.MinLength(1)]
        public Tag[] Tags { get; set; }
        
        /// <summary>
        /// Pet status in the store
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="status")]
        public PetStatus Status { get; set; }
        
        /// <summary>
        /// Type of a pet
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="petType")]
        public string PetType { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Tag
    {
        
        /// <summary>
        /// Tag ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Tag name
        /// Min length: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="name")]
        [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength=1)]
        public string Name { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum PetStatus
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        available = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        pending = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        sold = 2,
    }
    
    public partial class PetClient
    {        
        private System.Net.Http.HttpClient client;
        
        private JsonSerializerSettings jsonSerializerSettings;
        
        public PetClient(System.Net.Http.HttpClient client, 
                         JsonSerializerSettings jsonSerializerSettings=null)
        {
            if (client == null)
                throw new ArgumentNullException("Null HttpClient.", "client");

            if (client.BaseAddress == null)
                throw new ArgumentNullException("HttpClient has no BaseAddress", "client");

            this.client = client;
            this.jsonSerializerSettings = jsonSerializerSettings;
        }
        
        /// <summary>
        /// Add a new pet to the store
        /// Add new pet to the store inventory.
        /// AddPet pet
        /// </summary>
        /// <param name="requestBody">Pet object that needs to be added to the store</param>
        public async Task AddPetAsync(Pet requestBody, 
               Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet";
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri))
            {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create(jsonSerializerSettings);
            requestSerializer.Serialize(requestWriter, requestBody);
            var content = new StringContent(requestWriter.ToString(), 
                          System.Text.Encoding.UTF8, "application/json");
            httpRequestMessage.Content = content;
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
            }
        }
        
        /// <summary>
        /// Update an existing pet
        /// UpdatePet pet
        /// </summary>
        /// <param name="requestBody">Pet object that needs to be added to the store</param>
        public async Task UpdatePetAsync(Pet requestBody, 
          Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet";
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, requestUri))
            {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create(jsonSerializerSettings);
            requestSerializer.Serialize(requestWriter, requestBody);
            var content = new StringContent
            (requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            httpRequestMessage.Content = content;
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
            }
        }
        
        /// <summary>
        /// Find pet by ID
        /// Returns a single pet
        /// GetPetById pet/{petId}
        /// </summary>
        /// <param name="petId">ID of pet to return</param>
        /// <returns>successful operation</returns>
        public async Task<Pet> GetPetByIdAsync
        (long petId, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/"+petId;
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
                var stream = await responseMessage.Content.ReadAsStreamAsync();
                using (JsonReader jsonReader = new JsonTextReader
                                  (new System.IO.StreamReader(stream)))
                {
                var serializer = new JsonSerializer();
                return serializer.Deserialize<Pet>(jsonReader);
                }
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }
        
        /// <summary>
        /// Deletes a pet
        /// DeletePet pet/{petId}
        /// </summary>
        /// <param name="petId">Pet id to delete</param>
        public async Task DeletePetAsync
        (long petId, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/"+petId;
            using (var httpRequestMessage = 
                   new HttpRequestMessage(HttpMethod.Delete, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }
        
        /// <summary>
        /// Finds Pets by status
        /// Multiple status values can be provided with comma separated strings
        /// FindPetsByStatus pet/findByStatus
        /// </summary>
        /// <param name="status">Status values that need to be considered for filter</param>
        /// <returns>successful operation</returns>
        public async Task<Pet[]> FindPetsByStatusAsync(PetStatus[] status, 
               Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/findByStatus?"+String.Join
                             ("&", status.Select(z => $"status={z}"));
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
                var stream = await responseMessage.Content.ReadAsStreamAsync();
                using (JsonReader jsonReader = 
                       new JsonTextReader(new System.IO.StreamReader(stream)))
                {
                var serializer = new JsonSerializer();
                return serializer.Deserialize<Pet[]>(jsonReader);
                }
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }

As you may see, the generated codes are looking simpler than what was generated by other tools while providing similar capacity of data handling and error handling. More importantly, the data types matching is more comprehensive and precise.

Example of Generated TypeScript Codes for Angular 5+

JavaScript
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
export namespace My_Pet_Client {
    export interface ApiResponse {
        code?: number;
        type?: string;
        message?: string;
    }


    /** A representation of a cat */
    export interface Cat extends Pet {

        /** The measured skill for hunting */
        huntingSkill: CatHuntingSkill;
    }

    export enum CatHuntingSkill { clueless = 0, lazy = 1, adventurous = 2, aggressive = 3 }

    export interface Category {

        /** Category ID */
        id?: number;

        /**
         * Category name
         * Min length: 1
         */
        name?: string;

        /** Test Sub Category */
        sub?: CategorySub;
    }

    export interface CategorySub {

        /** Dumb Property */
        prop1?: string;
    }


    /** A representation of a dog */
    export interface Dog extends Pet {

        /**
         * The size of the pack the dog is from
         * Minimum: 1
         */
        packSize: number;
    }


    /** A representation of a honey bee */
    export interface HoneyBee extends Pet {

        /** Average amount of honey produced per day in ounces */
        honeyPerDay: number;
    }

    export interface Order {

        /** Order ID */
        id?: number;

        /** Pet ID */
        petId?: number;
        quantity?: number;

        /** Estimated ship date */
        shipDate?: Date;

        /** Order Status */
        status?: OrderStatus;

        /** Indicates whenever order was completed or not */
        complete?: boolean;

        /** Unique Request Id */
        requestId?: string;
    }

    export enum OrderStatus { placed = 0, approved = 1, delivered = 2 }

    export interface Pet {

        /** Pet ID */
        id?: number;

        /** Categories this pet belongs to */
        category?: Category;

        /** The name given to a pet */
        name: string;

        /**
         * The list of URL to a cute photos featuring pet
         * Maximum items: 20
         */
        photoUrls: Array<string>;
        friend?: Pet;

        /**
         * Tags attached to the pet
         * Minimum items: 1
         */
        tags?: Array<Tag>;

        /** Pet status in the store */
        status?: PetStatus;

        /** Type of a pet */
        petType?: string;
    }

    export interface Tag {

        /** Tag ID */
        id?: number;

        /**
         * Tag name
         * Min length: 1
         */
        name?: string;
    }

    export enum PetStatus { available = 0, pending = 1, sold = 2 }

    @Injectable()
    export class PetClient {
        constructor(@Inject('baseUri') private baseUri: 
        string = location.protocol + '//' + location.hostname + 
        (location.port ? ':' + location.port : '') + '/', private http: HttpClient) {
        }

        /**
         * Add a new pet to the store
         * Add new pet to the store inventory.
         * Post pet
         * @param {Pet} requestBody Pet object that needs to be added to the store
         * @return {void} 
         */
        AddPet(requestBody: Pet, headersHandler?: () => HttpHeaders): 
                                 Observable<HttpResponse<string>> {
            return this.http.post(this.baseUri + 'pet', 
                   JSON.stringify(requestBody), { headers: headersHandler ? 
                   headersHandler().append('Content-Type', 'application/json;charset=UTF-8') :
                   new HttpHeaders({ 'Content-Type': 'application/json;charset=UTF-8' }), 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Update an existing pet
         * Put pet
         * @param {Pet} requestBody Pet object that needs to be added to the store
         * @return {void} 
         */
        UpdatePet(requestBody: Pet, headersHandler?: () => HttpHeaders): 
                  Observable<HttpResponse<string>> {
            return this.http.put(this.baseUri + 'pet', JSON.stringify(requestBody), 
                   { headers: headersHandler ? headersHandler().append
                   ('Content-Type', 'application/json;charset=UTF-8') : 
                   new HttpHeaders({ 'Content-Type': 'application/json;charset=UTF-8' }), 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Find pet by ID
         * Returns a single pet
         * Get pet/{petId}
         * @param {number} petId ID of pet to return
         * @return {Pet} successful operation
         */
        GetPetById(petId: number, headersHandler?: () => HttpHeaders): Observable<Pet> {
            return this.http.get<Pet>(this.baseUri + 'pet/' + petId, 
                   { headers: headersHandler ? headersHandler() : undefined });
        }

        /**
         * Deletes a pet
         * Delete pet/{petId}
         * @param {number} petId Pet id to delete
         * @return {void} 
         */
        DeletePet(petId: number, headersHandler?: () => HttpHeaders): 
                  Observable<HttpResponse<string>> {
            return this.http.delete(this.baseUri + 'pet/' + petId, 
                   { headers: headersHandler ? headersHandler() : undefined, 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Finds Pets by status
         * Multiple status values can be provided with comma separated strings
         * Get pet/findByStatus
         * @param {Array<PetStatus>} status Status values that need to be 
         * considered for filter
         * @return {Array<Pet>} successful operation
         */
        FindPetsByStatus(status: Array<PetStatus>, headersHandler?: () => 
                         HttpHeaders): Observable<Array<Pet>> {
            return this.http.get<Array<Pet>>(this.baseUri + 'pet/findByStatus?' + 
                   status.map(z => `status=${z}`).join('&'), 
                   { headers: headersHandler ? headersHandler() : undefined });
        }

Optimized for What

Strongly Typed API and Client API

Strongly typed Web API provides better supports for business applications with complex semantic modelings and workflows. And client APIs have better to reflect such semantic modelings through strongly typed data. Similar to what WebApiClientGen can do, OpenApiClientGen can translate components and data types in Open API definitions as precise as possible. For more details, please check:

Remote Procedure Call

Swagger/Open API Specifications assumes RESTful designs, while WebApiClientGen and its spin-off OpenApiClientGen is optimized for RPC.

From a certain point of view, REST is a disciplined or constrained way of building RPC. For building complex business applications, REST may be beneficial to overall development, or may be too technical and forcing developers to translate high level business logic into REST, rather than to work on business domain modeling more directly.

“Consider how often we see software projects begin with adoption of the latest fad in architectural design, and only later discover whether or not the system requirements call for such an architecture.”

References

What Not Supported

What not supported is by design. This article explains this briefly.

HTTP Request Headers as Parameters

HTTP request headers are often for 2 purposes:

  1. Authorization
  2. Correlation ID and other meta data not semantically related to business models

Tools like NSwag will generated client API codes with function/operation parameters including headers if the Open API definition includes headers as parameters. If you like this, you may ignore OpenApiClientGen and continue to use NSwag or alike.

Upon mcp.yaml, NSwag generates such function prototype:

C#
public System.Threading.Tasks.Task<PatientClaimInteractiveResponseType> McpPatientclaiminteractiveGeneralV1Async(PatientClaimInteractiveRequestType body, string authorization, string dhs_auditId, string dhs_subjectId, string dhs_messageId, string dhs_auditIdType, string dhs_correlationId, string dhs_productId, string dhs_subjectIdType)
{

 

OpenApiClientGen generates a simpler function prototype:

C#
public async Task<PatientClaimInteractiveResponseType> McpPatientClaimInteractiveGeneralAsync(PatientClaimInteractiveRequestType requestBody, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
{

Without headers as function parameters, the client API functions look cleaner, the client application codes look more clearly structural, and you may be focused more on business logic rather than technical details of headers.

For authorization, typically you would use HTTP interception. And .NET HttpClient, Angular Http service and JQuery Ajax, etc. provide built-in support for HTTP interception.

For handling request headers if the Open API definition defines some, you may include "HandleHttpRequestHeaders": true in CodeGen.json when generating client API codes, and this will put a callback function at the end of the parameter list of the client API functions.

Others

Please check wiki.

Points of Interest

If you are developing ASP.NET Web API or .NET Core Web API, you don't need OpenApiClientGen because WebApiClientGen can generate a few sets of client APIs for C#, Angular 2+, Aurelia, jQuery, AXIOS and Fetch API without involving Swagger/Open API Specification. And you may be more interested in the following articles:

  1. Generate C# .NET Client API for ASP.NET Web API
  2. Generate TypeScript Client API for ASP.NET Web API
  3. ASP.NET Web API, Angular2, TypeScript and WebApiClientGen
  4. Generate C# Client API for ASP.NET Core Web API
  5. WebApiClientGen vs Swashbuckle plus NSwag

Remarks

The development of OpenApiClientGen had been started after the publishing of WebApiClientGen vs Swashbuckle plus NSwag while WebApiClientGen and OpenApiClientGen share some key components and design concepts.

This tool has been tested with over 1000 Open API definitions.

Having the generated codes to pass compilation proves the generated codes is not enough. And you cannot assume the generated client API for a respective Web API service is correct without building integration test suites which talk to the Web API service.

Extending OpenApiClientGen for Other JavaScript Libraries and Frameworks

Comparing with other client codes generators for Open API / Swagger, OpenApiClientGen is much easier to extend. If your favorite JavaScript libraries and frameworks are not included, you may extend ClientApiTsFunctionGenBase like ClientApiTsNG2FunctionGen and ControllersTsClientApiGenBase like ControllersTsNG2ClientApiGen. As you can see, each of the plugins involves around 200 lines of codes specifically.

History

  • 3rd July, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)