Enter The API Gateway


Enter The API Gateway

This week my family and I were supposed to be enjoying our summer vacations in sunny Chelan, WA, where this time of the year you can enjoy the not-so-cold waters of beautiful Lake Chelan. It's the closest you can get to a beach vacation around here.

Sadly, as it usually happens on that side of the mountain when it gets too hot, a bunch of wild fires started on the north side of the lake (and all around east Washington State), bringing smoke and bad air quality. So we decided to reschedule the trip for later in the year.

On the flip side, that allowed me to keep working on the .NET Developer Bootcamp project, this time diving into a series of prerequisites to get the Game Store system deployed to the cloud.

On to this week's update.

Getting ready for the cloud

Just for fun, and since I got the whole Game Store system running end to end locally, I decided to try to deploy it as-is to Azure via the .NET Aspire integration with the Azure Developer CLI (azd).

As expected, things didn't go quite smoothly. In fact, even azd crashed right away, which as I learned from trial and error, was due to version 1.9.5 of azd being broken:

Going back to version 1.9.3 fixed those.

After that, I was able to deploy things to Azure, but pretty much nothing was working. And that's because my Aspire app model was not ready for deployment.

So, to get this going in Azure, a bunch of infra dependencies need to be updated to their PaaS (platform as a service) counterparts:

  • PostgreSQL -> Azure Database for PostgreSQL
  • MongoDB -> Azure CosmosDB
  • Azurite -> Azure Storage
  • RabbitMQ -> Azure Service Bus
  • Keycloak -> Entra ID
  • Kafka -> ???

Kafka is interesting because there is no direct equivalent for it in the Azure cloud at least. The closest you can do is Azure Event Hubs, but from what I read you can't do topic compaction, which would complicate the event-carried state transfer pattern across the system.

Maybe the right thing to use there is the Confluent Cloud, but we'll get back to this later.

But how to enable the use of a simple Docker container for local development, while using a fully featured Azure service when deploying? All within the same Aspire App Model?

Something like this:

That should provision a local RabbitMQ container for local development, or an Azure Service Bus namespace at deployment time. And, whatever gets provisioned, is associated with the Catalog microservice as a "messagebroker" resource.

Now, that's the easy part, but how to make the Catalog microservice (and all your microservices) publish and consume messages to and from either RabbitMQ or Azure Service Bus without a bunch of if-else logic all over the place?

Well, using MassTransit! The amazing messaging framework that can abstract all those details from you. I cover MassTransit extensively in my microservices program, and it will be a core pillar of the new bootcamp too.

For instance, here's how the Catalog microservice configures the message broker via MassTransit on startup:

Notice the AddGameStoreMassTransit() method there. That one will figure out what broker to use (RabbitMQ or Azure Service Bus) based on the .NET Aspire configuration and also provides a delegate to further configure MassTransit as needed.

I'll explain how that all works in detail in the bootcamp, but for now there's a more pressing thing that we are still missing in this system.

Yet Another Reverse Proxy

I knew I had to introduce an API gateway sooner rather than later. After all, you don't want to have your frontend deal with the complexities of talking to multiple microservices all the time.

However, as I was trying to enable Entra ID as the identity provider for the system in the cloud, I faced a complication that prevented the frontend from being able to request access tokens for all microservices at once.

After playing with Entra ID for a while, I now know that what I was trying to do is actually doable in a slightly different way, but instead of keep going that route I thought it was a good time to take a step back and do the right thing by introducing an API gateway.

There are dozens of API gateways out there, and I have tried out both nginx and Emissary Ingress in the past, but for this project I really wanted to try out YARP (Yet Another Reverse Proxy), given how well it integrates with ASP.NET Core applications.

So, after trying it out for a few minutes, I was able to enable it for my project very easily, since it only requires a few lines of C# code and a few other lines of configuration, like this:

That goes into the API gateway project appsettings.json file and defines how to handle GET requests for the Catalog microservice.

So, when the frontend sends this request to the gateway:

The gateway will forward it as this request to the Catalog microservice (with all query parameters, payload and headers):

And this is the C# code you add to Program.cs to use that configuration:

In a similar way you can define routes for all your other microservices. But when it gets interesting is when you have to do some additional transformations to the incoming request that you can'd do via simple configuration.

For instance, to enable the Entra ID integration I had to perform a token exchange at the gateway so that the microservice would receive the correct access token. Something like this:

This is something we did all the time back at my last team at Microsoft (brings back memories!), and it makes total sense since in the real world your clients will present you an access token that grants them access to your gateway, nothing else. From there your gateway decides what kind of token to use to reach the target microservice.

This is how the system looks like after integrating the API gateway:

Notice how the frontend is now completelly unaware of what's going on behind the gateway, which is the whole point of this, because now you can move pieces as you please in the back without impacting the frontend and the frontend only needs to worry about talking to a single endpoint.

To close, let me tell you about one thing you may want to enable in your .NET Aspire projects.

Waiting for resources to start

One thing that I've been noticing as I run the system with .NET Aspire is how even when all projects report they are "Running" they are in fact reporting a bunch of warnings or errors trying to talk to their dependencies.

This is kind of surprising since, supposedly, all .NET Aspire components have health checks enabled, so I was expecting the orchestrator would honor them and not consider anything ready to go until health checks report healthy.

Sadly, seems like that's not the case, and you have to do a bit more to achieve the expected behavior. But fortunately there's a nice project from the great David Fowler that can help here.

The project is called WaitForDependenciesAspire, all open source here, and what it lets you do is have resources wait for their dependencies to be ready as reported by their health checks.

In terms of the code, looks like this:

That's the Catalog microservice waiting for its Postgres database to be ready before starting, and then the Frontend also waiting for the Catalog microservice to be ready.

Looks like this in the .NET Aspire dashboard:

I think it's a nice helper for local development, however I don't see how any of that will translate to actual heath checks that can be used in the cloud (or maybe I'm missing something?).

Anyways, I'll get back to finish that Entra ID configuration and hopefully start that Azure deployment for real this time.

Until next time!


Whenever you’re ready, there are 3 ways I can help you:

  1. Building Microservices With .NET:​ The only .NET backend development training program that you need to become a Senior .NET Backend Engineer.
  2. ASP.NET Core Full Stack Bundle: A carefully crafted package to kickstart your career as an ASP.NET Core Full Stack Developer, step by step.
  3. Promote yourself to 15,000+ subscribers: by sponsoring this newsletter.

11060 236th PL NE, Redmond, WA 98053
Unsubscribe · Preferences

The .NET Saturday

Join 16,000+ subscribers for actionable .NET, C#, Azure and DevOps tips. Upgrade your skills in less than 5 mins every week.

Read more from The .NET Saturday

The Pillars of Observability After completing the Game Store application, the last week was all about scripting the first few modules of the upcoming .NET Cloud Developer Bootcamp, which essentially means creating a detailed Word document for each lesson, describing exactly how that lesson should go. I don't know how many content creators do this, since it's a long (and sometimes tedious) process, but I find it essential to make sure each concept and technique is introduced at exactly the...

DevOps: Part 2 It's done! A couple of days ago I finally completed the Game Store system, the distributed .NET Web application that will drive the upcoming .NET Cloud Developer Bootcamp (Is that a good name? Let me know!). I'm amazed by how much the tech has advanced in the .NET and Azure world in the last few years. There's so much going on in this field that I have no idea how folks are solving today's chaotic puzzle to learn cloud development with .NET. I was lucky enough to enter the .NET...

DevOps: Part 1 Wow, getting the Game Store web application deployed to Azure via Azure DevOps was one of the most challenging things I've done so far as part of the .NET Developer Bootcamp project. But, somehow it all worked out, and the end result is really nice. The complexity came from me trying to fit both the Azure infra deployment and the CI/CD process into the .NET Aspire model, which is only poorly supported at this time. But, having worked on dozens of Azure deployments and CI/CD...