A code generator that creates strongly-typed C# extension methods on IHttpClientFactory from OpenAPI 3.x specifications.
- ✅ Parses OpenAPI 3.x documents (JSON and YAML)
- ✅ Generates extension methods on
IHttpClientFactory(not classes!) - ✅ Supports all HTTP methods: GET, POST, PUT, DELETE, PATCH
- ✅ Generates model/DTO classes from OpenAPI schemas
- ✅ Functional programming style with Result pattern
- ✅ Handles path parameters, query parameters, and request bodies
- ✅ 100% test coverage
Add the project reference to your project:
dotnet add reference path/to/RestClient.Net.OpenApiGenerator/RestClient.Net.OpenApiGenerator.csprojusing RestClient.Net.OpenApiGenerator;
// Load your OpenAPI specification
var openApiContent = File.ReadAllText("openapi.json");
// Generate code
var result = OpenApiCodeGenerator.Generate(
openApiContent,
@namespace: "MyApi.Client",
className: "MyApiExtensions"
);
// Write generated files
File.WriteAllText("MyApiExtensions.cs", result.ExtensionMethodsCode);
File.WriteAllText("Models.cs", result.ModelsCode);Given this OpenAPI specification:
{
"openapi": "3.0.0",
"info": {
"title": "Users API",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.example.com"
}
],
"paths": {
"/users": {
"get": {
"operationId": "getUsers",
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
},
"post": {
"operationId": "createUser",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
},
"/users/{id}": {
"get": {
"operationId": "getUserById",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
},
"delete": {
"operationId": "deleteUser",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"204": {
"description": "No Content"
}
}
}
}
},
"components": {
"schemas": {
"User": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
}
}
}
}
}
}The generator produces extension methods like:
using System.Net.Http.Json;
using System.Text.Json;
using RestClient.Net;
using RestClient.Net.Utilities;
using Urls;
namespace MyApi.Client;
/// <summary>Extension methods for API operations.</summary>
public static class MyApiExtensions
{
private static readonly AbsoluteUrl BaseUrl = "https://api.example.com".ToAbsoluteUrl();
/// <summary>GetUsers operation.</summary>
#pragma warning disable CA2000
public static Task<Result<List<User>, HttpError<string>>> GetUsers(
this IHttpClientFactory httpClientFactory,
CancellationToken ct = default
) =>
httpClientFactory
.CreateClient()
.GetAsync(
BaseUrl.WithRelativeUrl(new RelativeUrl("/users")),
DeserializeJson<List<User>>,
DeserializeError,
ct
);
#pragma warning restore CA2000
/// <summary>GetUserById operation.</summary>
public static Task<Result<User, HttpError<string>>> GetUserById(
this IHttpClientFactory httpClientFactory,
int id,
CancellationToken ct
) =>
httpClientFactory.CreateGet<User, string, int>(
url: BaseUrl,
getRelativeUrl: id => new RelativeUrl($"/users/{id}"),
deserializeSuccess: DeserializeJson<User>,
deserializeError: DeserializeError
)(id, ct);
/// <summary>CreateUser operation.</summary>
public static Task<Result<User, HttpError<string>>> CreateUser(
this IHttpClientFactory httpClientFactory,
User body,
CancellationToken ct
) =>
httpClientFactory.CreatePost<User, string, User>(
url: BaseUrl,
getRelativeUrl: _ => new RelativeUrl("/users"),
deserializeSuccess: DeserializeJson<User>,
deserializeError: DeserializeError,
deserializeRequest: CreateJsonContent
)(body, ct);
/// <summary>DeleteUser operation.</summary>
public static Task<Result<Unit, HttpError<string>>> DeleteUser(
this IHttpClientFactory httpClientFactory,
int id,
CancellationToken ct
) =>
httpClientFactory.CreateDelete<Unit, string, int>(
url: BaseUrl,
getRelativeUrl: id => new RelativeUrl($"/users/{id}"),
deserializeSuccess: static (_, _) => Task.FromResult(Unit.Value),
deserializeError: DeserializeError
)(id, ct);
// Helper methods...
}And model classes:
namespace MyApi.Client;
/// <summary>User.</summary>
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}using Microsoft.Extensions.DependencyInjection;
using MyApi.Client;
// Setup
var services = new ServiceCollection();
services.AddHttpClient();
var provider = services.BuildServiceProvider();
var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
// Use the generated extension methods
var usersResult = await httpClientFactory.GetUsers();
var userResult = await httpClientFactory.GetUserById(123, CancellationToken.None);
// Result pattern handling
usersResult.Match(
success => Console.WriteLine($"Got {success.Count} users"),
error => Console.WriteLine($"Error: {error.ErrorContent}")
);- ✅ GET (with and without path parameters)
- ✅ POST (with request body)
- ✅ PUT (with path parameters and request body)
- ✅ DELETE (with path parameters)
- ✅ PATCH (with path parameters and request body)
- ✅ integer (int)
- ✅ integer with format int64 (long)
- ✅ number (float)
- ✅ number with format double (double)
- ✅ string
- ✅ boolean
- ✅ arrays
- ✅ object references ($ref)
- ✅ Path parameters
⚠️ Query parameters (parsed but not yet fully implemented in URL generation)- ✅ Request body (JSON)
- ✅ Component schemas
- ✅ Schema references ($ref)
- ✅ Array types with item references
The generator follows functional programming principles:
- No Exceptions in Generated Code: All operations return
Result<TSuccess, HttpError<TError>> - Immutable Data: Uses records for result types
- Pure Functions: Extension methods are stateless
- Composability: Generated methods can be easily composed with other functional operations
The project includes comprehensive tests covering all functionality:
dotnet test RestClient.Net.OpenApiGenerator.Tests/RestClient.Net.OpenApiGenerator.Tests.csprojTest coverage includes:
- ✅ All HTTP methods (GET, POST, PUT, DELETE, PATCH)
- ✅ Path parameters (single and multiple)
- ✅ Request body handling
- ✅ Response type mapping
- ✅ Model generation
- ✅ Error handling
- ✅ Edge cases (no servers, invalid OpenAPI, etc.)
- Microsoft.CodeAnalysis.CSharp (4.11.0) - Code generation
- https://github.com/microsoft/OpenAPI.NET
This project follows the same license as the parent RestClient.Net project.