Documenting ASP.NET Core Health Checks with OpenAPI
Health endpoints are essential for building operable platforms. ASP.NET Core provides built-in health checks nowadays, but there was no way to provide OpenAPI specs for them. Let's have a look at an easy fix
Documenting ASP.NET Core Health Checks with OpenAPI
TL;DR - Health endpoints are essential for building operable platforms. ASP.NET Core provides built-in health checks nowadays, but there was no way to provide OpenAPI specs for them. Let's have a look at an easy fix.
Using health endpoints in your application is essential. My friend Toon Vanhoutte gives a couple of examples of why you should use them and how they can help get rid of cold starts.
What health endpoints do depends on your use case - It can be as simple as returning HTTP 200 OK or provide more information about your dependencies as well.
Let's have a look at how we can implement them!
Native support for health checks in ASP.NET Core
As of ASP.NET Core 2.2, they have native support for health checks which is built on top of AspNetCore.Diagnostics.HealthChecks which provides health probing for a ton of dependencies out-of-the-box!
It allows you to very easily add health checks to your application on startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddSqlServer(Configuration["Data:ConnectionStrings:Sql"])
.AddRedis(Configuration["Data:ConnectionStrings:Redis"]);
}
You can read this article by Scott Hanselman if you want to learn more about it.
I've been dying to use this on all my projects but there was one problem - It was not included in our generated OpenAPI specifications.
So I headed over to Swashbuckle.AspNetCore, which I use to generate OpenAPI specs, and found this nice feature request!
Unfortunately, the mechanics of health checks are totally different and not easy to integrate so it's up for grabs.
That doesn't really help me for now, but what's the issue anyway?
Why OpenAPI specifications are important
When building APIs, it is crucial for me that every endpoint is documented with OpenAPI specs.
There are a couple of reasons for that:
- You are ready if you want to use an API Gateway where you can import those to expose them via a more robust API platform
- It improves the customer experience when consuming your services as they guide you through your application
- It acts as living documentation elaborating on what features the API provides
One could say that health endpoint is different because not everybody should consume it! That is correct, but those that will use it still need to know how to call it and what to expect, so you have to document them as well.
So I started digging...
Recently I started a new project and I was sick of adding my own health point again, so I started digging!
It turns out that when you call AddHealthChecks
it adds a HealthCheckService
as a dependency.
This allows you to call all the configured health checks and get a report of their status.
A simple trick of dependency injection, et voila:
[Route("api/v1/health")]
public class HealthController : Controller
{
private readonly HealthCheckService _healthCheckService;
public HealthController(HealthCheckService healthCheckService)
{
_healthCheckService = healthCheckService;
}
/// <summary>
/// Get Health
/// </summary>
/// <remarks>Provides an indication about the health of the API</remarks>
/// <response code="200">API is healthy</response>
/// <response code="503">API is unhealthy or in degraded state</response>
[HttpGet]
[ProducesResponseType(typeof(HealthReport), (int)HttpStatusCode.OK)]
[SwaggerOperation(OperationId = "Health_Get")]
public async Task<IActionResult> Get()
{
var report = await _healthCheckService.CheckHealthAsync();
return report.Status == HealthStatus.Healthy ? Ok(report) : StatusCode((int)HttpStatusCode.ServiceUnavailable, report);
}
}
It's still a bit verbose but it's perfect for me - Not only can I now document my health endpoint, but it also allows me to delegate health checks to the framework.
It looks silly but saves me a lot of trouble.
How about non-HTTP applications?
One downside of the ASP.NET Core's health checks is that it's limited to HTTP only, so if you are building a queue processor you have to write your own TCP-based health check. This is not an easy thing to do and would be great if this comes out-of-the-box allowing us to choose how to expose it.
Frankly, this is a big gap because there are a lot of applications which can benefit from this where people don't even bother to implement it themselves because it's too much work.
Now that the container world is booming it is even becoming more and more important - Health endpoints are used, for example, to check if the app is still running fine; if not they kill it and start a new instance! But what if you don't have those endpoints and the processor goes into an unstable status, nothing will be processed anymore.
The good thing is that it's already on their radar, but unfortunately not coming very soon.
Conclusion
Health endpoints are essential for building operable platforms, they allow you to gain insight on how your application is doing and if it's ready to serve traffic.
We've learned how we can provide a well-documented health endpoint which is served by health information provided by ASP.NET Core.
Thanks for reading,
Tom.
Splash image by Nathan Anderson