During a recent team retrospective at Linn I took on the task of devising an approach through which we could document our HTTP APIs. In this post I discuss the first step towards this in the form of metadata module support in NancyFx.

The Goal

My aim is to make use of a framework such as Swagger to provide browsable documentation. I’m keen to keep it lightweight, and ideally generated dynamically wherever possible. I would also like it to be declared as close to the code itself (without getting in the way). This will hopefully minimise the possibility of the documentation becoming out of date as an API evolves.

The Approach

Around the same time that I started researching this, a discussion opened up on the Nancy GitHub project on the topic of generating API documentation. So I chimed in and found that Kristian Hellang had started spiking out an implementation of Nancy.Swagger. At the time - the support for associating metadata with Nancy routes was fairly limited. Implementing something like Swagger would require a much richer set of data, and a convenient way of associating it with a given route.

Based on an idea by Kristian, I spiked out a method for declaring route metadata at the point of defining the route itself. Here’s what that looked like:

Get["/{name}", null, meta =>
{
    meta.Description("Greets the given name);
    meta.Parameter("name").Description("The name to greet.").Required();
}] = parameters =>
{
    return "Hello " + parameters.name;
};

But that turned out to be sort of clunky, and the feedback I got from the Nancy team was less than enthusiastic.

Steve Robbins then suggested that by introducing the concept of named routes, we could declare route metadata separately from the route definition itself - but make it easy to tie the two together. This led to the idea of implementing a metadata module buddy class for each Nancy module, providing the relevant metadata.

With this idea, I implemented what ended up becoming a new NuGet called Nancy.Metadata.Module, which provides the appropriate infrastructure for implementing metadata modules. The package will be released as part of Nancy 0.23.

The Implementation

Given a Nancy module which defines a named route:

public class ProductsModule : NancyModule
{
	public ProductsModule() : base("products")
	{
		Get["GetAllProducts", "/"] = _ => "Here are the products";
	}
}

If you define an IMetadataModule implementation alongside the module, following the naming convention below, then you can describe the named routes in more detail. A base class MetadataModule makes this easier to implement.

public class ProductsMetadataModule : MetadataModule<MySuperRouteMetadata>
{
	public ProductsMetadataModule()
	{
		Describe["GetAllProducts"] = desc =>
		{
			return new MySuperRouteMetadata(desc.Method, desc.Path)
			{
				SuperDescription = "Returns the string \"Here are the products\""
			};
		};
	}
}

The parameter passed to the delegate is of type RouteDescription - so you can access any of those properties in constructing the metadata to return.

The following conventions are supported out of the box for locating and naming a metadata module:

NancyModule Relative MetadataModule
{name}Module {name}MetadataModule
{name}Module Metadata.{name}MetadataModule
{namespace}.{name}Module Metadata.{name}MetadataModule


To retrieve the metadata take a dependency on IRouteCacheProvider, and use it as follows:

routeCacheProvider
	.GetCache()
    .RetrieveMetadata<MyRouteMetadata>()

The sample project Nancy.Demo.Hosting.Aspnet in the Nancy repository includes a basic example.