# API Versioning with Asp.Net Core Minimal APIs

# API Versioning

API versioning is the practice of managing changes to an API without breaking the client’s applications when new versions are developed. Versioning is an integral part of the API design. A good API versioning strategy communicates the changes made and allows clients using an existing REST API and only migrate or update their applications to the recent versions when they are ready.

## Types of API Versioning

There are many ways to version an API, each approach has its own set of strengths and weaknesses in addressing changes based on their scope. This article will cover the three following ways.

### URI

This is the most natural and straightforward approach. It includes the version number in the URL path and is often done with the prefix "v".

```csharp
http://www.example.com/api/v1/todos
http://api.example.com/v1/todos
```

### Query Parameter

In this approach the version number is added as a query param making it easier to switch to the newest version in case a query parameter is not specified.

```csharp
http://www.example.com/api/todos?version=1
```

### Custom Headers

This technique adds custom headers with version numbers as an attribute. This approach differs from query and URI versioning because it doesn’t add filler content to the URI.

```csharp
Accept-version: v1
Accept-version: v2
```

# Implementation

To start the implementation first it is necessary to add the nugget package [Asp.Versioning.Http](https://www.nuget.org/packages/Asp.Versioning.Http/). Once it is added we will be able to use the extension methods \*AddApiVersioning \* to define the strategy, *WithApiVersionSet* to define the version of the endpoint and *HasApiVersion* to define with versions are supported by the endpoint.

## Configuration

The following code is creating the version numbers that will be used in the version set system.

```csharp
var version1 = new ApiVersion(1);
var version2 = new ApiVersion(2);
```

The version set tells the API versioning which versions are available to be used by the endpoints.

```csharp
var versionSet = app.NewApiVersionSet()
                     .HasApiVersion(version1)
                     .HasApiVersion(version2)
                     .Build();
```

## URI

For this approach it is necessary to modify the route adding a new parameter to MapGet, MapPost or MapPut methods. It will be added a new segment to the URL called version that is limited to the type apiVersion. This is a validation created by the versioning system to ensure that correct values are passed.

```csharp
app.MapGet("v{version:apiVersion}/todoitems", [Authorize] async (TodoDb db) =>
{
    return await db.Todos.ToListAsync();
})
.WithApiVersionSet(versionSet)
.HasApiVersions(new[] { version1, version2 });
```

It is also necessary to configure the type of versioning that API will use. The following code shows how.

```csharp
builder.Services.AddApiVersioning(opt =>
{
    opt.ApiVersionReader = new UrlSegmentApiVersionReader();
});
```

![Screenshot 2022-11-27 094540.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669560356825/_1_PFkXW_.png align="left")

## Query Parameter

For this approach It is not necessary to change the route of the endpoint, it only adds the versioning type that will be used.

```csharp
builder.Services.AddApiVersioning(opt =>
{
    opt.ApiVersionReader = new QueryStringApiVersionReader("version");
});
```

![Screenshot 2022-11-27 095651.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669561044160/6pPdOqwdw.png align="left")

## Custom Headers

This approach also does not require changing the route and it offers two ways to accomplish the versioning, using an extension to the Accept header or using a custom header.

### Media Type

```csharp
builder.Services.AddApiVersioning(opt =>
{
    opt.ApiVersionReader = new MediaTypeApiVersionReader("version");
});
```

![Screenshot 2022-11-27 100751.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669561682582/MyO3DlI13.png align="left")

### Custom Header

```csharp
builder.Services.AddApiVersioning(opt =>
{
    opt.ApiVersionReader = new HeaderApiVersionReader("Api-Version");
});
```

![Screenshot 2022-11-27 101818.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669562307096/d-3PwQkHI.png align="left")

## Testing

So now we can have two different versions of the same endpoint with different behaviors. In the following example the endpoint *todoitems* has version 1 with the old implementation and version 2 that changes the property *CompletedTimestamp*.

```csharp
app.MapPost("v{version:apiVersion}/todoitems", [Authorize] async (IValidator<Todo> validator, Todo todo, TodoDb db) =>
{
    ValidationResult validationResult = await validator.ValidateAsync(todo);

    if (!validationResult.IsValid)
    {
        return Results.ValidationProblem(validationResult.ToDictionary());
    }

    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithApiVersionSet(versionSet)
.HasApiVersion(version1);
```

![Screenshot 2022-11-27 165312.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669586001099/xgXWZcjnX.png align="left")

```csharp
app.MapPost("v{version:apiVersion}/todoitems", [Authorize] async (IValidator<Todo> validator, Todo todo, TodoDb db) =>
{
    ValidationResult validationResult = await validator.ValidateAsync(todo);

    if (!validationResult.IsValid)
    {
        return Results.ValidationProblem(validationResult.ToDictionary());
    }

    todo.CompletedTimestamp = DateTime.Now;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithName("newtodoitems")
.WithApiVersionSet(versionSet)
.HasApiVersion(version2);
```

![Screenshot 2022-11-27 165359.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669586049692/WY59sVBpj.png align="left")

## Errors

The API will return an error when a version is required and was not specified or when the endpoint does not support the version used.

![Screenshot 2022-11-27 102239.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669562578246/A7iYn0xma.png align="left")

![Screenshot 2022-11-27 102215.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1669562585856/bTZPuSBhl.png align="left")

When the API version is not specified, it can be defined a default version that will be used.

```csharp
builder.Services.AddApiVersioning(opt =>
{
    opt.ApiVersionReader = new HeaderApiVersionReader("Api-Version");
    opt.DefaultApiVersion = version1;
    opt.AssumeDefaultVersionWhenUnspecified = true;
});
```

# Wrapping Up

You can find the full code on my [GitHub](https://github.com/jhonatanfernando/api-fluent-validator).
