The .NET Stacks #44: šŸ¦ APIs that are light as a feather

This week, we discuss the FeatherHttp project and Azure Static Web Apps.

Dave Brock
Dave Brock

Happy Monday! Hereā€™s what weā€™re talking about this week:

  • One big thing: Looking at the FeatherHttp project
  • The little things: Azure Static Web apps with DevOps, new OSS badges, coding tip
  • Last week in the .NET world

One big thing: Looking at the FeatherHttp project

Weā€™ve talked in the past about ASP.NET Core MVC APIs and their role in the .NET Core ecosystem. While MVC has received performance improvements and does what it sets out to do, it carries a lot of overhead and is often reminiscent of the ā€œhere you go, have it allā€ reputation of .NET Framework. Itā€™s a robust solution that allows you to build complex APIs but comes with a lot of ceremony. With imperative frameworks like Go and Express, you can get started immediately and with little effort. No one has said the same about writing APIs in ASP.NET Core. Itā€™s a bad look on the framework in general, especially when folks want to try out .NET for the first time.

Last year, Dapr pushed cross-platform samples for the major frameworks, and this tweet shows a common theme with MVC:

Can we have solutions that allow you to get started quickly and avoid this mess? What if you arenā€™t a fan of controllers? You could use external libraries like MediatR or API Endpoints, or a framework like Nancyā€”but it still feels like the native runtime deserves better. The ASP.NET Core team has thought about this for a while.

The route-to-code alternative is a great start, which I wrote about. It allows you to write simple JSON APIs, using an API endpoints modelā€”with some helper methods that lend a hand.

Hereā€™s a quick example:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsJsonAsync(new { message = $"Hello {name}!" });
    });
});

Itā€™s great, but Microsoft will tell you itā€™s only for simple APIs. Their docs clearly state: Route-to-code is designed for basic JSON APIs. It doesnā€™t have support for many of the advanced features provided by ASP.NET Core Web API. This begs the question: will .NET ever have a modern, lightweight API solution that has a low barrier to entry and also scales? Can I have a lightweight API that starts small and allows me to add complex features as I need them?

That is the goal of FeatherHttp, a project from ASP.NET Core architect David Fowler. Triggered by Kristianā€™s tweet, Fowler says the repo has three key goals: to be built on the same primitives on .NET Core, to be optimized to build HTTP APIs quickly, and having the ability to take advantage of existing .NET Core middleware and frameworks. According to a GitHub comment, the solution uses 90% of ASP.NET Core and changes the Startup pattern to be more lightweight. You can also check out a tutorial that walks you through building the backend of a React app with basic CRUD APIs and some serialization.

Hereā€™s an example:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

var app = WebApplication.Create(args);

app.MapGet("/", async http =>
{
    await http.Response.WriteAsync("Hello World");
});

await app.RunAsync();

Is this a fun experiment (currently at version 0.1.82-alpha) or will it make its way into ASP.NET Core? Iā€™m not a mind reader, my friends, but I do know two things: (1) David Fowler is the partner architect on ASP.NET Core, and (2) big objectives for .NET 6 are to appeal to new folks and to improve the developer inner-loop experience. I suspect weā€™ll be hearing a lot more about this. Stay tuned.


The little things: Azure Static Web apps with DevOps, new OSS badges, code complexity tip

Last week, Microsoft announced that Azure Static Web Apps supports deployment through Azure DevOps YAML pipelines. I wrote about it as well. It opens doors for many corporate customers who arenā€™t ready to move to GitHub yetā€”while theyā€™ve made great strides, GitHub still has some work to do to match the robust enterprise capabilities of Azure DevOps.

I was able to move one of my projects over seamlessly. Unlike GitHub Actions, Azure DevOps handles PR triggers for you automatically, so my YAML is pretty clean:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: AzureStaticWebApp@0
    inputs:
      app_location: "BlastOff.Client"
      api_location: "BlastOff.Api"
      output_location: "wwwroot"
    env:
      azure_static_web_apps_api_token: $(deployment_token)

While it isnā€™t as streamlined and elegant as the GitHub experienceā€”you need to configure your deployment token manually, and you donā€™t get automatic staging environmentsā€”this should help improve adoption. If youā€™re wondering whether to use Azure Static Web Apps with Azure DevOps or GitHub, Iā€™ve got you covered.


The ASP.NET Core team has introduced ā€œgood first issueā€ and ā€œHelp wantedā€ GitHub badges. If youā€™ve ever wanted to contribute to ASP.NET Core but didnā€™t know where to start, this might help.


Sometimes, it seems that processing a collection of objects is half of a developerā€™s job. It can often be a subject of abuse, especially when it comes to nested loops and their poor performance.

Hereā€™s an example of me iterating through a list of Blogger objects, checking if a Url exists, and adding it to a list. (Iā€™m using records and target-typed expressions for brevity.)

using System.Collections.Generic;

var result = new List<string>();
var bloggers = new List<Blogger>
{
    new("Dave Brock", "https://daveabrock.com", true),
    new("James Clear", "https://jamesclear.com", false)
};

foreach (var blogger in bloggers)
{
    if (blogger.IsTechBlogger)
    {
        var url = blogger.Url;
        if (url is not null)
            result.Add(url);
    }
}

record Blogger(string Name, string Url, bool IsTechBlogger);

Instead, try a LINQ collection pipeline:

using System.Collections.Generic;
using System.Linq;

var urlList = new List<string>();
var bloggers = new List<Blogger>
{
    new("Dave Brock", "https://daveabrock.com", true),
    new("James Clear", "https://jamesclear.com", false)
};

urlList = bloggers.Where(b => b.IsTechBlogger)
                  .Select(u => u.Url)
                  .Where(u => u is not null).ToList();

record Blogger(string Name, string Url, bool IsTechBlogger);

šŸŒŽ Last week in the .NET world

šŸ”„ The Top 4

šŸ“¢ Announcements

šŸ“… Community and events

šŸŒŽ Web development

šŸ„… The .NET platform

ā›… The cloud

šŸ“” Languages

šŸ”§ Tools

šŸ“± Xamarin

šŸ— Design, testing, and best practices

šŸŽ¤ Podcasts

šŸŽ„ Videos

.NET Stacks