Azure Functions are fast becoming one of my favourite pieces of technology, they are perfect when used in an event driven architecture and their ease of development, deployment, their ability to scale on demand and consumption based pricing make them perfect for a number of use cases. Azure Cosmos DB is also an extremely versatile data store that is extremely easy to get up and running, making it a perfect companion for Azure Functions.
One use case that I haven’t really explored before is using Azure Functions to create REST APIs. The HTTP trigger allows us to trigger a function through a HTTP request so wouldn’t it be brilliant if we could build a REST API using Azure Functions where we would only be charged for each individual request against the API rather than deploying to an App Service that is going to have an ongoing monthly cost associated with it regardless of usage?
Within Azure Functions and even ASP.NET Web API there is always a certain amount of boilerplate code that usually ends up being repeated for each and every method.
Wouldn’t it be great if there were a way to remove this boilerplate and declare our APIs using a simple fluent, code-based API? This is where Function Monkey (https://github.com/JamesRandall/FunctionMonkey) from James Randall (https://www.azurefromthetrenches.com/) comes in. We’ll also be using Cosmonaut (https://github.com/Elfocrash/Cosmonaut) from Nick Chapsas (http://chapsas.com/) to simplify our data access against Cosmos DB.
In this example I’d like to create a REST API to perform CRUD operations against a Cosmos DB collection called “process” that will be responsible for keeping track of when a process begins and completes. Our API will need to expose the following endpoints:
- POST: /process
- PUT: /process/{id}/succeeded
- PUT: /process/{id}/failed
- GET: /process/{id}
- GET: /process?status={status}
The implementation of the API is described below and all code can be found on my GitHub at https://github.com/benjarvis18/AzureFunctionsRESTApi.
What is Function Monkey?
Function Monkey provides a fluent API around Azure Functions to allow us to easily define functions without being tightly coupled to the Azure Functions runtime itself. The framework allows us to implement a commanding pattern where we define commands (or queries) with handlers and then have a mediator dispatch the commands to their handlers as they are received. The beauty of this is our handlers, where our logic sits, are decoupled from the underlying trigger meaning we could easily configure our application so the handler is triggered by a queue rather than a HTTP request or even take our code out of Azure Functions and put it into a full-blown ASP.NET Web API with minimal changes.
Azure Functions doesn’t currently have dependency injection built in natively (there are some workarounds available) however, Function Monkey has this functionality baked in using the ASP.NET Core DI library meaning we can easily make our code testable. It’s also easy to configure validation using the built-in integration with the popular FluentValidation library.
The Code
Commands
Commands provide the input to our functions so they are a logical place to begin.
Each command implements the ICommand marker interface and defines a set of properties that will be passed in to the functions and provided to our command handlers. They are POCOs so there is no complication within them, the object I have defined as my CreateProcessCommand.
Handlers
Our command handlers are where the magic happens! Their responsibility is to take the command that is passed into the function and process it.
Each handler implements the ICommandHandler interface with T being the type of the command that the handler is handling.
The handler supports dependency injection so in our case the constructor accepts an instance of ICosmosStore which is our Cosmos DB “repository” that is provided by Cosmonaut to allow us to perform operations against Cosmos DB.
The command itself is processed in the ExecuteAsync method, in this case we are creating an instance of our Process entity and adding it to Cosmos DB using the cosmos store provided by Cosmonaut.
Bringing It All Together – Configuration
All of the configuration of Function Monkey is done through a class that implements the IFunctionAppConfiguration interface; this class is used by the Function Monkey compiler to generate the required C# assembly that will contain our functions.
The IFunctionHostBuilder that is passed into our configuration class exposes a fluent API that allows us to define our functions.
The first step is to call the Setup method which allows us to configure our dependencies for dependency injection using the provided service collection and register our command handlers; in my case I am using the Discover method provided by the command registry to locate and register any command handlers in my assembly.
The next method (OpenApiEndpoint) configures the Open API definition for our API and configures the Swagger user interface that will allow us to explore and use our API later on.
The final step is to define the configuration for our functions. This blog post is only covering HTTP functions however, other triggers are described in the documentation at https://functionmonkey.azurefromthetrenches.com.
The API we are building in this example only covers a single resource so we are defining a single route called “/api/v1/process”; within this route we can define multiple functions. Each function is linked to a command and we can specify the HTTP method the function will represent (GET, POST, PUT etc) and a custom route for the function. We can also specify route parameters, which are useful when we want to pass through an ID or other parameter (see the function definition for GetProcessByIdQuery as an example).
As you can see, the configuration is extremely simple with minimal code and no complicated JSON configuration files to deal with. The fluent API makes things clear and easy to understand.
Observations
One thing I came across that’s worth noting is that the class that implements IFunctionAppConfiguration isn’t suitable for retrieving any configuration that needs to be retrieved at runtime as the class is called at build time.
As an example, when configuring Cosmonaut I originally defined my FunctionAppConfiguration class like below where I used Environment.GetEnvironmentVariable to get the values for the Cosmos DB database name, endpoint and auth key. However, when I tried to debug the application the Function Monkey compiler failed because GetEnvironmentVariable returned null. This isn’t the behaviour I wanted because I wanted the values to be retrieved at runtime similar to if they were defined in the ASP.NET Startup class.
To get around this I created a static CosmosStoreFactory class with a method to retrieve an instance of CosmosStore, using Environment.GetEnvironmentVariable to get the configuration values.
I then registered CosmosStore in the DI container like below, using CosmosStoreFactory to instantiate it.
GetCosmosStore is only called at runtime so the function configuration has been loaded at that point meaning the correct connection details are retrieved.
Running Our Application
To run my functions locally I can simply hit F5 and Function Monkey will build my functions and the functions emulator will start.
We configured an Open API endpoint for our function app so if I navigate to http://localhost:7071/openapi/ I get access to Swagger UI where I can see the endpoints that have been defined and also try calling some of the endpoints.
Now, if I want to try out the POST method I can select it and click “Try it Out”, from there I can fill in the details for my request like below:
Once I click the “Execute” button a request will be fired off to the server and I can see that a 200 (OK) HTTP response is returned meaning the operation was successful:
Now, if I go to the document explorer in my Cosmos DB emulator at https://localhost:8081/_explorer/index.html I can see that the document has been created.
As you can see, the code involved in creating our API is extremely minimal when compared to creating an API in Azure Functions without Function Monkey. The local development / debugging experience is great and it would be extremely easy to get this solution deployed to Azure, with the only difference being that we would need to pass an authentication key when calling our API endpoints.
Conclusion
To conclude, Azure Functions are an excellent choice when developing a REST API if you want a service that is easy to scale and cheap to run. Combined with Cosmos DB as a data store it’s extremely easy to have something simple working in a CRUD scenario within a matter of hours. Function Monkey makes the whole process painless and allows you to decouple your commands and handlers from the underlying triggers making them easy to test and giving more flexibility if commands need to be processed differently as the application grows.
As always, if there are any questions then please get in touch or contact me on Twitter via @BenJarvisBI.