ApiContracts 2.2.2

dotnet add package ApiContracts --version 2.2.2
NuGet\Install-Package ApiContracts -Version 2.2.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ApiContracts" Version="2.2.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ApiContracts --version 2.2.2
#r "nuget: ApiContracts, 2.2.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ApiContracts as a Cake Addin
#addin nuget:?package=ApiContracts&version=2.2.2

// Install ApiContracts as a Cake Tool
#tool nuget:?package=ApiContracts&version=2.2.2

ApiContracts

Package CI & CD Build Version

What is ApiContracts?

ApiContracts is a tool designed to help developers create contract specifications on the entity models of their API endpoints. This allows for validation of incoming request DTOs against those contracts, ensuring that only DTOs containing all the data/properties as defined by the contract are accepted.

Features

  • Contract Creation: Define contract specifications for your entity models with ease.
  • Request Validation: Validate incoming request DTOs against the defined contracts.
  • Data Integrity: Ensure that all incoming DTOs contain the necessary data/properties.

How do I get started?

Server

  1. First, define your contracts by inheriting from the Contract abstract class
namespace SampleAPI.Contracts;

public class BasicUserContract : Contract
{
    public override string Name => "BasicUserContract";
}

public class FullUserContract : Contract
{
    public override string Name => "FullUserContract";
}
  1. Then, use the ContractBound attribute to define your entity model under contract
namespace SampleAPI.Models;

[ContractBound]
public class SampleModel
{

}
  1. Provide the acceptance criteria for your contract specifications using the Acceptance<T> attribute, declaring if the value is required or optional by default.
namespace SampleAPI.Models;

[ContractBound]
public class SampleModel
{
    [Acceptance<BasicUserContract>(Required = true)]
    [Acceptance<FullUserContract>(Required = true)]
    public string? Name { get; set; }

    [Acceptance<BasicUserContract>(Required = true)]
    [Acceptance<FullUserContract>(Required = true)]
    public int Age { get; set; }

    [Acceptance<BasicUserContract>(Required = true)]
    [Acceptance<FullUserContract>(Required = true)]
    public string? Email { get; set; }

    [Acceptance<FullUserContract>()]
    public string? PhoneNumber { get; set; }

    [Acceptance<FullUserContract>(Required = true)]
    public string? Address { get; set; }

    [Acceptance<FullUserContract>()]
    public string? City { get; set; }

    [Acceptance<FullUserContract>(Required = true)]
    public string? State { get; set; }
}
  1. Register ApiContracts on your web API configuration pipeline using the UseApiContracts() extension method.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
});

builder.Services.AddApiContractDocs();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseApiContracts(); // Register ApiContracts
app.UseHttpsRedirection();

app.MapEndpoints();

app.Run();
  1. Optionally, you can assign the Swagger/OpenAPI documentation like supported
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
});

builder.Services.AddApiContractDocs(); // Add ApiContractDocs

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseApiContracts();
app.UseHttpsRedirection();

app.MapEndpoints();

app.Run();
  1. Assign the SampleModel as your API entity model (here's a minimal API example)
namespace SampleAPI.Extensions;

public static class Endpoints
{
    public static void MapEndpoints(this IEndpointRouteBuilder routes)
    {
        routes.MapPost("/test", ([FromBody] SampleModel model) =>
        {
            if (model is null)
                return Results.BadRequest("Invalid model.");

            return Results.Ok(model);
        })
        .WithName("GetTest")
        .WithOpenApi()
        .Produces(StatusCodes.Status400BadRequest)
        .Produces<SampleModel>(StatusCodes.Status200OK);
    }
}

Client

Once you have setup your API with a ContractBound entity model, the endpoint will be 'under-contract'. This means you need to provide a service name on the custom Contract-Name header with your request.

TypeScript Sample Client

interface SampleModel {
    // Define the properties of SampleModel here
}

async function callTestEndpoint(model: SampleModel) {
    const response = await fetch('https://your-api-url/test', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Contract-Name': 'FullUserContract' // Define your target contract here
        },
        body: JSON.stringify(model)
    });

    if (!response.ok) {
        const message = `An error has occurred: ${response.status}`;
        throw new Error(message);
    }

    const data: SampleModel = await response.json();
    return data;
}

// Usage:
const model: SampleModel = {
    // Populate the model properties here
};

callTestEndpoint(model)
    .then(data => console.log(data))
    .catch(error => console.error(error));

C# Sample Client

public static async Task CallTestEndpoint(User model)
{
    var json = JsonConvert.SerializeObject(model);
    var data = new StringContent(json, Encoding.UTF8, "application/json");

    client.DefaultRequestHeaders.Add("Contract-Name", "FullUserContract"); // Define your target contract here

    var response = await client.PostAsync("https://your-api-url/test", data);

    string result = response.Content.ReadAsStringAsync().Result;
    Console.WriteLine(result);
}

Where can I get it?

First, install NuGet. Then, install ApiContracts from the package manager console:

PM> Install-Package ApiContracts

Or from the .NET CLI as:

dotnet add package ApiContracts

Do you have an issue?

If you're still running into problems, please don't hesitate to file an issue in this repository. We appreciate your feedback and contributions!

License

ApiContracts Copyright © 2024 Anthony Larner under the MIT license.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.2 62 4/21/2024
2.2.1 63 4/16/2024
2.2.0 57 4/16/2024
2.1.22 63 4/16/2024
2.1.22-beta.38 41 4/16/2024
2.1.22-beta.36 37 4/16/2024
2.1.22-alpha.36 40 4/16/2024
2.1.22-alpha.32 45 4/16/2024
2.1.15 64 4/12/2024