API Conventions and Docs

Back

Loading concept...

🏢 API Conventions & Documentation in ASP.NET

Making Your API Speak Clearly to Everyone


🎭 The Restaurant Menu Analogy

Imagine you own a restaurant. Your kitchen can make amazing dishes, but customers need to know:

  • What dishes are available? (API endpoints)
  • Which version of the menu are you looking at? (API versioning)
  • What happens if something goes wrong? (Problem details)
  • What does each dish contain? (Request/Response formats)

Your API documentation is like a perfect menu that explains everything clearly!


📌 What We’ll Learn

graph TD A["API Conventions & Docs"] --> B["API Versioning"] A --> C["Problem Details"] A --> D["ProblemDetails Middleware"] A --> E["API Conventions"] A --> F["Swagger Integration"] A --> G["OpenAPI Specification"] A --> H["Produces & Consumes"]

🔢 API Versioning

What Is It?

Think of API versioning like phone models. iPhone 14 is different from iPhone 15. But old apps still work on iPhone 14!

API versioning lets you update your API without breaking apps that use the old version.

Why Do We Need It?

Imagine you have a toy. You want to make it better. But your friend is still playing with the old toy. You don’t want to break their toy!

Versioning = Making new toys while keeping old toys working

How to Add Versioning

Step 1: Install the Package

// Add this NuGet package
dotnet add package Asp.Versioning.Http

Step 2: Configure in Program.cs

builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
})
.AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
});

Step 3: Use Version in Controllers

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetV1()
        => Ok("Version 1 Products");
}

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ProductsV2Controller : ControllerBase
{
    [HttpGet]
    public IActionResult GetV2()
        => Ok("Version 2 Products - New & Improved!");
}

How Users Call Different Versions

Method Example URL
URL Path /api/v1/products
Query String /api/products?api-version=1.0
Header X-Api-Version: 1.0

⚠️ Problem Details

What Is It?

When something goes wrong, how do you tell the user?

Problem Details is a standard way to say “something went wrong” in a clear, organized format.

It’s like when a teacher marks your test wrong. They don’t just write “X”. They explain what was wrong and how to fix it!

The Standard Format (RFC 7807)

{
  "type": "https://example.com/errors/out-of-stock",
  "title": "Product out of stock",
  "status": 400,
  "detail": "Product 'Blue Shoes' is currently unavailable",
  "instance": "/products/123"
}

What Each Part Means

Field What It Does Example
type Link to error documentation "/errors/not-found"
title Short error name "Not Found"
status HTTP status code 404
detail Full explanation "User with ID 5 not found"
instance Where error happened "/users/5"

Creating Problem Details Response

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    var product = _db.Products.Find(id);

    if (product == null)
    {
        return Problem(
            type: "/errors/not-found",
            title: "Product Not Found",
            statusCode: 404,
            detail: quot;Product with ID {id} does not exist"
        );
    }

    return Ok(product);
}

🛡️ ProblemDetails Middleware

What Is It?

The ProblemDetails Middleware is like a safety net.

When your code throws an error, this middleware catches it and turns it into a nice, clean Problem Details response.

No more ugly error pages! Just clean, helpful messages.

Setting It Up

Step 1: Add the Service

// In Program.cs
builder.Services.AddProblemDetails();

Step 2: Use the Middleware

// In Program.cs - add EARLY in pipeline
var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

Step 3: Customize (Optional)

builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = context =>
    {
        context.ProblemDetails.Extensions["traceId"] =
            context.HttpContext.TraceIdentifier;

        context.ProblemDetails.Extensions["timestamp"] =
            DateTime.UtcNow;
    };
});

What Happens Now?

Before Middleware:

500 Internal Server Error
(ugly stack trace)

After Middleware:

{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "An error occurred",
  "status": 500,
  "traceId": "abc-123-xyz",
  "timestamp": "2024-01-15T10:30:00Z"
}

đź“‹ API Conventions

What Are They?

API Conventions are like promises you make to users of your API.

“I promise this endpoint will return these things!”

They help generate better documentation automatically.

Built-in Conventions

ASP.NET has ready-made conventions:

[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
public class ProductsController : ControllerBase
{
    // Conventions applied to all actions!
}

What DefaultApiConventions Include

Method Name Expected Responses
Get 200 OK, 404 Not Found
Post 201 Created, 400 Bad Request
Put 204 No Content, 400, 404
Delete 200 OK, 400, 404

Using Conventions on Methods

[ApiConventionMethod(
    typeof(DefaultApiConventions),
    nameof(DefaultApiConventions.Get))]
public IActionResult GetProduct(int id)
{
    // Convention tells Swagger:
    // "This can return 200 or 404"
}

Creating Custom Conventions

public static class MyApiConventions
{
    [ProducesResponseType(200)]
    [ProducesResponseType(404)]
    [ProducesResponseType(500)]
    public static void Get(int id) { }

    [ProducesResponseType(201)]
    [ProducesResponseType(400)]
    public static void Create(object model) { }
}

🌊 Swagger Integration

What Is Swagger?

Swagger is like a playground for your API!

It creates a beautiful webpage where anyone can:

  • See all your endpoints
  • Try them out live
  • Understand what data to send

Setting Up Swagger

Step 1: Add the Package

dotnet add package Swashbuckle.AspNetCore

Step 2: Configure Services

// In Program.cs
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My Cool API",
        Version = "v1",
        Description = "An API that does cool things!"
    });
});

Step 3: Add Middleware

// In Program.cs
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint(
            "/swagger/v1/swagger.json",
            "My API V1");
    });
}

Step 4: Visit the UI

Go to: https://localhost:5001/swagger

You’ll see a beautiful interactive page!

Adding XML Comments

Make your Swagger even better with comments:

/// <summary>
/// Gets a product by its ID
/// </summary>
/// <param name="id">The product ID</param>
/// <returns>The product details</returns>
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    // Your code
}

Enable in .csproj:

<PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

📜 OpenAPI Specification

What Is OpenAPI?

OpenAPI is the recipe book format that Swagger uses.

It’s a standard way to describe your API in a file. Like a blueprint for a house!

The Format

OpenAPI files are written in JSON or YAML:

openapi: 3.0.1
info:
  title: My API
  version: v1
paths:
  /api/products:
    get:
      summary: Get all products
      responses:
        '200':
          description: Success

Key Components

graph TD A["OpenAPI Document"] --> B["Info"] A --> C["Paths"] A --> D["Components"] B --> B1["Title"] B --> B2["Version"] C --> C1["Endpoints"] C --> C2["Methods"] D --> D1["Schemas"] D --> D2["Security"]

Why Use OpenAPI?

Benefit Description
Auto-generate clients Create code for any language
Documentation Always up-to-date docs
Testing Tools can test your API
Mocking Create fake servers for testing

Customizing OpenAPI Output

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Products API",
        Version = "v1",
        Contact = new OpenApiContact
        {
            Name = "Support",
            Email = "help@example.com"
        },
        License = new OpenApiLicense
        {
            Name = "MIT"
        }
    });
});

📦 Produces and Consumes

What Do They Mean?

  • Produces = What your API sends back (output)
  • Consumes = What your API accepts (input)

Like a vending machine:

  • Consumes: Coins (input)
  • Produces: Snacks (output)

Using Produces Attribute

[HttpGet("{id}")]
[Produces("application/json")]
[ProducesResponseType(typeof(Product), 200)]
[ProducesResponseType(typeof(ProblemDetails), 404)]
public IActionResult GetProduct(int id)
{
    // Returns JSON
}

Using Consumes Attribute

[HttpPost]
[Consumes("application/json")]
[ProducesResponseType(typeof(Product), 201)]
[ProducesResponseType(400)]
public IActionResult CreateProduct(
    [FromBody] Product product)
{
    // Accepts JSON input
}

Common Content Types

Type What It Is
application/json JSON data
application/xml XML data
text/plain Plain text
multipart/form-data File uploads

Multiple Types

[HttpGet]
[Produces("application/json", "application/xml")]
public IActionResult GetProducts()
{
    // Can return JSON or XML
    // Based on Accept header from client
}

Setting Globally

// In Program.cs
builder.Services.AddControllers(options =>
{
    options.Filters.Add(
        new ProducesAttribute("application/json"));
    options.Filters.Add(
        new ConsumesAttribute("application/json"));
});

🎯 Putting It All Together

Here’s a complete example using everything we learned:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// API Versioning
builder.Services.AddApiVersioning(opt =>
{
    opt.DefaultApiVersion = new ApiVersion(1, 0);
    opt.ReportApiVersions = true;
});

// Problem Details
builder.Services.AddProblemDetails();

// Swagger/OpenAPI
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddControllers();

var app = builder.Build();

app.UseExceptionHandler();
app.UseSwagger();
app.UseSwaggerUI();
app.MapControllers();

app.Run();
// ProductsController.cs
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
[ApiController]
[Produces("application/json")]
[ApiConventionType(typeof(DefaultApiConventions))]
public class ProductsController : ControllerBase
{
    /// <summary>
    /// Gets a product by ID
    /// </summary>
    [HttpGet("{id}")]
    [ProducesResponseType(typeof(Product), 200)]
    [ProducesResponseType(typeof(ProblemDetails), 404)]
    public IActionResult Get(int id)
    {
        var product = FindProduct(id);
        if (product == null)
            return Problem(
                title: "Not Found",
                statusCode: 404);
        return Ok(product);
    }
}

🌟 Summary

Concept Purpose One-Liner
API Versioning Multiple API versions “iPhone 14 and 15 together”
Problem Details Standard error format “Clear error messages”
ProblemDetails Middleware Auto error handling “Safety net for errors”
API Conventions Standard response promises “Promises about responses”
Swagger Interactive API docs “API playground”
OpenAPI API description format “Recipe book format”
Produces/Consumes Input/output formats “What goes in and out”

🚀 You Did It!

Now your APIs can:

  • âś… Support multiple versions
  • âś… Give clear error messages
  • âś… Have beautiful documentation
  • âś… Tell the world what they accept and return

Your API is now like a 5-star restaurant with a perfect menu! 🍽️

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.