API Documentation

Live on this server

Generates an Open API definition and serves Swagger UI, Redoc and Scalar.

Documents the book shelf API with an Open API definition and three viewers to explore and try it: Swagger UI, Redoc and Scalar. AddOpenApi() generates the definition from the service itself - its paths, parameters and the shape of a book - so it is always up to date.

The viewers are served by GenHTTP and need no internet connection. Functional handlers and controllers are documented the same way.

To run it, create a console app (dotnet new console), add GenHTTP (dotnet add package GenHTTP.Full), replace Program.cs with the code below and start it with dotnet run - it answers at http://localhost:8080/.

Program.cs70 lines
using GenHTTP.Engine.Internal;

using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.ApiBrowsing;
using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.OpenApi;
using GenHTTP.Modules.Practices;
using GenHTTP.Modules.Reflection;
using GenHTTP.Modules.Webservices;

var app = Layout.Create()
                .AddService<BookService>("books")
                .AddOpenApi() // GET /openapi.json (or .yaml)
                .AddSwaggerUi() // GET /swagger/
                .AddRedoc() // GET /redoc/
                .AddScalar(title: "Book API"); // GET /scalar/

await Host.Create()
          .Handler(app)
          .Defaults()
          .RunAsync();

public record Book(int Id, string Title);

public class BookService
{
    private static readonly List<Book> Books = [new(1, "Lord of the Rings")];

    [ResourceMethod]
    public List<Book> List()
    {
        lock (Books) return [..Books];
    }

    [ResourceMethod(":id")]
    public Book Get(int id)
    {
        lock (Books)
        {
            return Books.FirstOrDefault(b => b.Id == id) ?? throw new ProviderException(ResponseStatus.NotFound, $"There is no book with id {id}");
        }
    }

    [ResourceMethod(Method.Put)]
    public Book Create(Book book)
    {
        lock (Books)
        {
            if (Books.Count >= 50)
            {
                throw new ProviderException(ResponseStatus.Conflict, "The shelf is full, please delete a book first");
            }

            var added = book with { Id = Books.Select(b => b.Id).DefaultIfEmpty().Max() + 1 };

            Books.Add(added);

            return added;
        }
    }

    [ResourceMethod(Method.Delete, ":id")]
    public void Delete(int id)
    {
        lock (Books) Books.RemoveAll(b => b.Id == id);
    }

}

Try it

Send the requests to the sample running on this server - change the path, headers or body as you like, or copy them as curl commands.

  1. Swagger UI

    PAGE /demo/api-docs/swagger/ Open
  2. Redoc

    PAGE /demo/api-docs/redoc/ Open
  3. Scalar

    PAGE /demo/api-docs/scalar/ Open
  4. Fetch the Open API definition

    GET
    curl -i 'https://genhttp.org/demo/api-docs/openapi.json'
  5. Fetch a book that does not exist

    404, as documented in the definition

    GET
    curl -i 'https://genhttp.org/demo/api-docs/books/42'