Refitter

repository·main·Indexed 19 days ago

https://github.com/christianhelle/refitter

A code generation tool for creating C# REST API clients using the Refit library from OpenAPI specifications. It supports integration via CLI, MSBuild, and Source Generators, and can generate Refit interfaces, data contracts, and dependency injection registration helpers. It also provides formatting options for Apizr (v6+) compatibility and supports custom property naming policies and transient error handling configurations.

Tokens
41.2K
Snippets
106
Records
148
Agent score
60%

What's inside Refitter

  1. What is Refitter

    main

    Refitter is a tool that generates C# REST API clients (specifically Refit interfaces and their contracts) directly from OpenAPI specifications. It provides three ways to integrate into your workflow:

    1. CLI Tool: For manual or script-based generation.
    2. MSBuild Task: For integration into your build process.
    3. C# Source Generator: For real-time, compile-time generation within your IDE.

    All three modes are driven by the same .refitter settings configuration.

  2. Overview of Refitter

    main

    Refitter is a code generation tool designed to create C# REST API Clients using the Refit library. It automates the creation of Refit interfaces and data contracts by consuming OpenAPI specifications.

    Key capabilities include:

    • Generating Refit interfaces and contracts from OpenAPI specs.
    • Formatting generated interfaces for compatibility with Apizr (v6+).
    • Generating registration helpers for dependency injection.

    Refitter is available in three distribution modes depending on your workflow requirements.

  3. How Refitter MSBuild tasks work

    main

    The Refitter.MSBuild package provides a custom .target file that automatically executes the RefitterGenerateTask.

    1. Scanning: The task scans your project directory for .refitter files.
    2. Execution: It executes the Refitter CLI to generate Refit interfaces (and optionally Apizr-compatible code).
    3. Integration: The generated .cs files are automatically added to the <Compile> ItemGroup so they are included in your build.

    By default, RefitterAutoScan is set to true, meaning code is regenerated automatically during normal builds. You can trigger generation manually using the RefitterGenerate target.

    dotnet build -t:RefitterGenerate
  4. Handle ApiException and status codes

    main

    Refitter throws an ApiException when a request returns a non-success HTTP status code. You should catch this exception to handle specific error scenarios based on the status code returned by the server.

    Common error mappings observed in API definitions:

    • 400 Bad Request: Invalid ID, invalid input, or invalid username/password.
    • 404 Not Found: Resource (Pet, Order, or User) not found.
    • 405 Method Not Allowed: Invalid input for specific operations.
  5. Generate a single client from multiple OpenAPI specifications

    main

    If you need to merge multiple API versions or different OpenAPI documents into a single Refit client, use the openApiPaths array instead of openApiPath.

    When using openApiPaths, the documents are merged into a single client. The first specification in the array serves as the base. Paths, component schemas, definitions, and tags from subsequent documents are merged into it. If duplicates exist (such as the same path key or schema name), the entry from the first document is preserved.

    {
      "openApiPaths": [
        "/path/to/your/openAPI/v1",
        "/path/to/your/openAPI/v2"
      ],
      "namespace": "Org.System.Service.Api.GeneratedCode"
    }
  6. Enable polymorphic serialization

    main

    To leverage native System.Text.Json inheritance serialization (which improves performance and handles derived types correctly during deserialization), enable the use-polymorphic-serialization setting in your configuration.

    When enabled, Refitter uses attributes like [JsonPolymorphic] and [JsonDerivedType] to ensure that if a base type is encountered, the correct derived type is instantiated based on a type discriminator (e.g., $type). This also prevents StackOverflowException in NSwag when encountering unknown types.

    [JsonPolymorphic(TypeDiscriminatorPropertyName = "$type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType, IgnoreUnrecognizedTypeDiscriminators = true)]
    [JsonDerivedType(typeof(TextTool), typeDiscriminator: "TextTool")]
    [JsonDerivedType(typeof(BrushTool), typeDiscriminator: "BrushTool")]
    public partial class Tool
    {
    }
  7. Generate multiple interfaces using multipleInterfaces: "ByTag"

    main

    When working with large APIs, you can use the multipleInterfaces setting to split the generated code into several interfaces (e.g., by API tag). When "multipleInterfaces": "ByTag" is set in the .refitter configuration, the generated ConfigureRefitClients() extension method will contain registration logic for every generated interface (e.g., IPetApi, IStoreApi, IUserApi), allowing you to register all of them with a single call to services.ConfigureRefitClients().

    {
      "openApiPath": "../OpenAPI/v3.0/petstore.json",
      "namespace": "Petstore",
      "multipleInterfaces": "ByTag",
      "dependencyInjectionSettings": {
        "baseUrl": "https://petstore3.swagger.io/api/v3",
        "httpMessageHandlers": [ "TelemetryDelegatingHandler" ],
        "transientErrorHandler": "Polly",
        "maxRetryCount": 3,
        "firstBackoffRetryInSeconds": 0.5
      }
    }
  8. Use [Query] for filtering and collection parameters

    main

    The [Query] attribute is used to include parameters in the URL query string.

    • Single values: Use [Query] Type name for simple filters (e.g., Status? status).
    • Collections: To pass multiple values for a single query key, use [Query(CollectionFormat.Multi)] IEnumerable<string> name. This allows for formats like tag1,tag2,tag3 or repeated keys depending on the implementation.
    • Form Data/Query mix: In POST requests, [Query] parameters are appended to the URL, while [Body] is sent in the request body.
  9. Use TUnit for testing in Refitter

    main

    Refitter uses TUnit for unit testing instead of xUnit. TUnit is optimized for faster test execution. When writing tests for new features or bug fixes, use the [Test] attribute provided by TUnit. Do not use xUnit's [Fact] or [Theory] attributes.

    // Use TUnit attribute instead of xUnit
    [Test]
    public async Task MyTest() 
    {
        // ...
    }
  10. Configure Refitter using .refitter files

    main

    Refitter uses .refitter files (JSON format) to define how REST API clients are generated from OpenAPI specifications. You can specify a single OpenAPI file via openApiPath or multiple files via openApiPaths to merge them into a single client.

    Key configuration areas include:

    • Namespaces: namespace and contractsNamespace.
    • Naming: naming.interfaceName (e.g., MyApiClient becomes IMyApiClient) and naming.useOpenApiTitle.
    • Generation Toggles: generateContracts, generateClients, generateXmlDocCodeComments, and generateDeprecatedOperations.
    • Output Control: outputFolder, outputFilename, and generateMultipleFiles.
    • Filtering: includeTags, includePathMatches, and excludeNamespaces (using regex).
    • API Behavior: returnIApiResponse, useCancellationTokens, and useIsoDateFormat.
    {
      "openApiPaths": [
        "/path/to/your/openAPI/v1",
        "/path/to/your/openAPI/v2"
      ],
      "namespace": "Org.System.Service.Api.GeneratedCode"
    }