Dotnet api from openapi-v3 specification

Dotnet api from openapi-v3 specification
Photo by Ilya Pavlov / Unsplash

Creating an HTTP .NET service from an OpenAPI-v3 specification

When you are working with multiple teams in your organization, you usually start of by declaring the communication interface between your services. For HTTP APIs you usually resort to communicating using an OpenAPI v3 specification, and what is a better way to create your service than to auto-generate as much as possible?

Our new product Heim has the possibility to do just this. We can generate all the boilerplate directly from the specification for a .NET project.

What is Heim?

Heim is a development tool for turning small functions into services, in different programming languages like Rust, C-sharp, Typescript and Python.

These applications can be used in the Heim runtime either locally, for testing purposes, or in our soon-to-be released Heim-Cloud environment.

What are the benefits of the Heim ecosystem, then? Well, it’s light-weight, cross-platform and cross-architecture, and allows for different teams to use just their language, for their application.

All applications run as isolated containerized environments, inside of the Heim runtime, as WebAssembly components.

Why should you use Heim?

If you’ve ever worked with cloud APIs in the past, you’ll know that building one can entail plenty of overhead: It’s never just about the core functionality, but will also come with a fair bit of extra work in the form of writing the boilerplate necessary to get a basic API up and running, and deployed.

Unlike the traditional solutions you’ve tried, Heim will let you focus on the core functionality of your application, and will create your boilerplate code, as well as deploy your application, with minimal input from you, once you have the code you actually want to write in place.

The applications you write and test locally can be deployed to the cloud without extra configuration, so you can concentrate on functionality, instead of infrastructure.

Since the Heim applications can be written in multiple different languages, you can let your team develop their application in the language that’s best suited for them.

So what do we need to get started?

First we need the Heim developer tooling. Right now, Heim Developer is in a closed beta, but the tool will soon be available to the public.

We also need the toolchain for the language we want to write our application/functions in. For the example used in this article, we will use the .NET 9 SDK, and Visual Studio Code as our code editor.

Our application will be compiled into a WebAssembly component, with help from the componentize-dotnet project.

The generator will create async-methods, and will make it possible to use almost any existing NuGet-package, since we won't be limited to only blocking operations in the function calls. This is made possible by our Heim-specific generation of method calls, since native WebAssembly compilation in .NET doesn’t yet support async functionality, as of when this article is written.

Writing our OpenAPI specification

I started off with the idea of playing around a little with geolocation coordinates, and using image metadata to find out what time the sunrise and sunset is happening in a specific location.

With this in mind, I want to generate support for the following operations in my API:

  1. Retrieve a country based on geolocation coordinates.
  2. Retrieve the time for the sunrise and sunset for specific geolocation coordinates on a specified date.
  3. Retrieve geolocation coordinates from the Exif-metadata of an uploaded image.

So, I first created a short OpenAPI specification, with the required operations. This simple OpenAPI specification will help us create our C-sharp application, and it will also help us generate calls to this API (using Bruno for manual testing). Of course, it’s possible to use whatever tool you want to test/call your finalized application, since it will be a regular REST-api.

I started off simple, with the bare minimum of functionality I thought I might need:

Initializing the application, using our Heim tooling

We initialize the .NET project with the following commands, pointing to our OpenAPI-specification:

Heim now prompts us to provide the following values:

  • Language: C#
  • Project-name: demo-geolocation
  • Version: 1.0.0
  • Base-path: /demo
Creating an Heim application scaffold from an openapi-specification
Creating an Heim application scaffold from an openapi-specification

Implementing the C-sharp project

We’ve now created the project “demo-geolocation”. Next, we’ll open it in Visual Studio Code, and make sure that it compiles and is working, with the dotnet build command.

Generated scaffolding for our operations

Our OpenAPI specification has been scaffolded into the GeoLocation.cs file, and we have placeholders for function implementations of all defined operations. The name GeoLocation is taken from the tag “geo-location” inside the openapi-specification, since all our operations have this tag. If no tag is provided, a single file based on the project name will be generated, this makes it possible to split your operation into different classes in your generated project. All incoming and outgoing data types are generated in the ITypes namespace. For example, we have the return type of ITypes.GeoLocationResponse, which is returned from our ExtractGeoLocationAsync operation.

Next, we need to figure out how we are going to create the functionality needed for the three core operations I want the application to handle. But first, we’ll create a simple authentication method, since all our operations will get an authorization token/secret supplied to them through the request headers.

Implementing authorization

We will keep our authentication code simple, and just check if the string provided is the same as our hardcoded secret “mysecret-token”. We’ll add a static function for handling the secret to our GeoLocation.cs class. This function checks if the values match, and will return true or false based on this.

Now, we have a very simple authorize function we can call when performing all our operations, and we can move on with the core functionality for our application.

Extracting coordinates from image

Since we only do this to try out the Heim C-sharp functionality, we will use an already existing NuGet-package – MetadataExtractor.

Uploaded images will be posted to our API as base64-encoded strings. Therefore, we need to decode those base64 strings and convert them into memory streams, which will be read by the MetadataExtractor library as images.

We’ll attempt to extract the GPS-coordinates from the loaded image, and return them if we can read them, otherwise we will just return a null value.

A simple solution for handling incoming images-as-strings would look like this:

This simple solution works well here, and we can use the coordinates to call our other API methods later.

Note that we didn’t need to do any serialization, or to create any response types manually. The Heim tool generated all the necessary boilerplate, using the OpenAPI-specification.

Getting the country name from a set of coordinates

Now, we want to look up which country these coordinates originate from.

To keep it simple, we will use the OpenCage Geocoding API. OpenCage offers a free trial for testing purposes, and this will work perfectly for the test application we’re building. So, we’ll sign up and receive an API-key, and use that when making calls to the OpenCage service.

The OpenCage API only needs us to provide coordinate sets, along with the API key, in order to provide us with large subsets of geolocation data.

However, since we are only interested in the country, we will keep it simple and only grab the country data we need from their response. So, next we write a function that performs an API-call, and extracts the “possible” country value from the json, and returns that value to a presumptive caller:

Note that this function is lacking proper error-handling, and shouldn’t be used as-is in production code. However, it’ll do for our purposes, and with this we should be able retrieve the country from our coordinates.

Retrieve Sunrise and sunset time

Now, we only have one more core operation to implement before we can test our application. It’s time to find out the sunrise and sunset times for a specific set of coordinates.

For this operation, we will try out the free Sunrise-Sunset API. The Sunrise-Sunset API doesn’t require us to generate an API-token, so we’ll be able to keep things very simple when using the API.

This operation will almost mirror the OpenCage API call, since we just need to collect another value from an HTTP response:

Build and deploy the application with Heim

Before we can deploy our application to our local Heim-server, we need to add the external URLs we will access from our application to the allowed-urls-list. The reason for this is that all Heim applications run in a zero-trust, sandbox-mode. Therefore, we need to declare our capabilities for calling external services, and this is done in the auto-generated deployment_unit.toml file we’ll find in the directory where we previously created our application:

Now, we are ready to build and deploy our application, and this process is pretty straightforward:

  1. We start our Heim-server with the command: heim start
  2. We run the build-and-deploy command in a separate terminal: heim build —deploy —local
Starting Heim runtime, and deploying the Heim application to the local Heim runtime

After running the required commands, we now have our demo geolocation application up and running on our local machine at http://127.0.0.1:3000/demo/, and we can start testing the API with the help of our specification and Bruno.

Testing our API

For testing the API, I selected a picture of the castle in Heim’s hometown Örebro, in Sweden.

This is the image we will use:

An image of Örebro Castle with geolocation coordinates added to the image metadata

We load the openapi.json file into Bruno, and that’s all we need to start making our request to our local API.

Next, we’ll encode the image to a base64 string, and put that string in the request body for the extract-geo-location operation.

This is the result of the operation:

Uploading the image to the extract-geo-location API

As you can see, we managed to get the coordinates from the image, and we can now use these coordinates for calling our two other operation endpoints.

Read on to see the results from the other operations the application is capable of.

Get-country:

Testing the get-country API operation with the coordinates from the earlier uploaded image.

Retrieve-Sunrise-And-Sunset:

Testing the sun-times operation with the coordinates from the earlier uploaded image.

As you can see, all our operations work as intended. This really shows how easy Heim is to use; Heim lets us concentrate on implementing core functionality already specified in the OpenAPI-specification, without having to deal with creating the basic boilerplate needed to get a fully functional API up and running.

Conclusion

We created and tested a backend API, based on an OpenAPI specification we created. We could focus on the core functionality of the API we wanted to create, while Heim provided the boilerplate we needed for our application, with minimal input from us.

In the future Heim will support the possibility to convert your generate Openapi-project to be served like an gRPC or Graphql-service. It will be possible to use the specifications for either of these service and select how you want to serve it.

The application we’ve created is executed inside the Heim runtime, and can be sent to our colleagues as long as they have the Heim developer tools installed, letting them get up and running within minutes.

The application will work on all platforms the Heim developer tool is available on, like Linux, Windows and macOS, so your frontend team can develop against your backend API without the need for having the tooling or source code for compiling the backend server, and also without the need to set up a docker environment or similar.

And this is just scratching the surface of what Heim will offer. With Heim Cloud launching soon, the time to explore this new way of developing APIs is now. Sign up for our beta and be part of the future of cloud-native development

💡
Soon you will be able to download Heim, and see for yourself how easy we can make creating your next API.

All code written and display in this article is available in this Github gist.

Written by Andreas Söderberg

Read more