Whether cloud or on-premise, you usually use a dedicated compute instance to run your server when providing public APIs. But provisioning dedicated compute instances, e.g., on Azure, is costly. Besides the basic running costs, the more traffic your API generates, the more additional costs it generates.
The question is: Can we provide public REST-style APIs and with high performance and at the same time reducing our monthly costs?
Here is where cloud functions come into play. These are small pieces of code that can be deployed independently and only pay for your Function when running.
For that, I want to look at:
Well, as you might know from math class in school, "a function is a process that associates each element of a set X, to a single element of a set Y"(Wikipedia). We can often see a function as a black box with defined input and a defined output from the outside. The function is just doing something to create the result from the given information.
In programming, a function (or method) is preferably a single action. The only concern of this process is to fulfill its action. Why are we writing functions? It makes code reusable and provides some modularity.
Many cloud providers offer special compute services like cloud functions where you don't care about the underlying infrastructure or even your tech stack for a couple of years. Then, you write the critical business logic. Your cloud provider handles the rest. For example, today, you often read about the serverless solution. Of course, there are servers behind this computing service, but you don't care about the infrastructure or the platform.
One of the most prominent serverless cloud offerings is Azure Functions by Microsoft. It allows you to create serverless architectures by connecting different functions. You can also integrate other Azure services into your serverless architecture, like CosmosDB.
Azure Functions provides writing functions in different programming languages, e.g., C#, Java, or python, and it supports Windows and Linux.
There are endless scenarios für Azure functions, like running scheduled tasks, analyzing IoT data streams, or building a web (REST) API.
Before we check out how to implement a REST API with Azure Functions, I want to discuss REST briefly. If you already have a basic understanding of REST, you can, of course, skip this part.
REST is the short form for Representational state transfer and is a software architectural style for providing a standard communication format between computer systems. It was created by Roy Fielding and it is one of the different approaches for using HTTP as a web communication protocol.
There are some distinct architectural constraints when using REST:
For further information about REST, read Roy Fielding's dissertation about REST.
Of course, as a developer, you sometimes want to be in control of your infrastructure. But in the long run, this costs you a lot of time and money.
It gets even worse when your organization wants to run services on-premise. Here it gets more complicated because you have to compare the costs for buying your hardware (CapEx) against the expense of just running your services on the cloud (OpEx).
One of the main advantages of Azure Functions is high scalability. Due to its serverless nature, you can scale your functions to up to 200 instances, depending on your App Service plan. As a result, it ensures high performance even if the load is very high.
The other advantage is pricing. Of course, this varies with your plan, but you only pay when clients consume your Functions when using the Consumption plan. It gives you a lot of flexibility, and you don't have to worry about paying for a Function when it is not in use.
Imagine we want to provide a digital database for a Bookstore. So instead of entering the bookstore and finding out that the book you want is currently unavailable, you can search online and check if your book is available. Additionally, someone working at the bookstore wants to update the database frequently. So it is always up-to-date.
With this simple example, you can create a simple Azure Function that handles requests by different clients. You need to provide a specific API that clients can use to access your function.
There are several ways to create Azure functions:
For this example, we go with Visual Studio. For setting up your local development environment for creating Azure Function apps in Visual Studio, check out the Quickstart Azure Functions docs.
After you set everything up, you can create a new Azure Functions project. Of course, we could create an entire software architecture, but today I want to focus on the Azure Function part.
First, let's create a new Azure Functions project Bookstore.Api with an HttpTrigger. After creation, your project should probably look like this:
When looking at the Function1 class you probably see something like this:
GIST: https://gist.github.com/adrinamin/87ad38dd67d82d2bded0618e77a476dd.js
There is, of course, a lot of boilerplate in the above code. We have one static class, called Function1, with one static method called Run. The most important part is the attributes inside the function class.
The Attribute FunctionName is necessary for the Function Runtime to find your function. Otherwise, you'll get a message like this when running your function app:
The second important attribute is the trigger. In our case, it's the HttpTrigger. You submit all necessary metadata information to the attribute (AuthorizationLevel, request methods, and Routing) and provide for the HttpRequest parameter of your function.
For testing purposes, you can set the authorization level to anonymous. However, in production scenarios, you should set it to function. Then, you have to set a new function key either inside the Azure Portal or through the Azure CLI. You can find more information about securing your functions here.
When adding input or output bindings to connect to other services, e.g., Azure Table storage, you add a new parameter to your method with the corresponding attribute for the metadata, similar to your trigger attribute.
So these are essential pieces of information to make your Functions App run. How you name your class or your method and what kind of information your method returns is not vital for running your Functions App. Azure Functions checks for the mentioned attributes.
When rerunning your function App with the given attributes, you see something like this in your console:
When running your app in Visual Studio, it spins up a simple localhost server with the Azure Functions Runtime environment through the Azure Functions Core Tools. You can copy the URL and paste it into your browser, and you will see something like this:
Because our Function listens to GET and POST requests, you can also pass information as a query string or request body to the Function.
So now that we know the basic structure of the function app, how can we create a REST-style API with Azure Functions?
First of all, we need to define the operations we want to provide with our API. Below is a list of the methods of our Bookstore.Api is supposed to provide:
Because we have an HttpTrigger and we want to create a REST interface, we can translate the above operations into REST routes:
We are, of course, totally free in choosing the right name for our routing. The global settings in host.json handle the "/api"base path prefix. If you want to change this, you can add the following:
{% c-block language="cs" %}
"extensions": {
"http": {
"routePrefix":""
}
}
{% c-block-end %}
So now the question is, how to implement our REST functions?
GIST: https://gist.github.com/adrinamin/70bdcbacc14dc30958301412d24ceffa.js
In the above sample code, I only focus on the REST API and not on any database connection. But you can, of course, connect any database to your Function.
As you might already have seen in the code above, we need to set the REST routes inside the HttpTrigger Attribute. Depending on how we connect to a database, we also need to add an id parameter to a function. In some cases, this is unnecessary, e.g., when connecting to CosmosDB via Input or Output Binding.
Additionally, we need to update the default function names inside the FunctionName attribute.
When using the POST or PUT method for adding or updating a book, we are using the request body from the HTTP request. The request body contains the book object in JSON format, and you deserialize it into a book object with the System.Text.Json.JsonSerializer library.
In general, you don’t have to rename the class or give the method a distinctive name. As already mentioned, Azure Functions only focuses on the attributes. But for better readability, it is, of course, recommended to use good descriptive names. The methods should, at least, have the same name as the Function itself.
There is a high amount of freedom in how you want to structure the classes of your Function App. You can even split the above class into two class files. As long as the methods have their corresponding attributes, the Function App will find the functions.
When running your Function App, the console will probably show you something like in the picture above. Now you can work with those functions and even extend their functionality if you want.
Because functions are just methods, you can, of course, write Tests for them. You can find a lot of information about testing your functions at the Microsoft docs. Keep in mind, though; these are not actual Unit Tests. Your functions usually are asynchronous and often return an ActionResult object if you are using HttpTrigger. The tests might work, but you are not able to mock your database. In this case, you need to check if you have some kind of test database locally and your CI/CD pipeline.
Depending on your business logic, it might be easier to extract the testable code from the asynchronous code with the Humble Object pattern. Then you can mock the volatile parts of your app, and you can Unit Test your business logic. If you want to know more about it, check out my post about the Humble Object pattern here.
Of course, you can also test your functions manually. You can either use postman or the azure portal. But in the end, this is very time-consuming. You don't want to test all your functions manually, especially when having more extensive applications. So it is helpful to take automated testing of your Functions into consideration.
If you want to bring your API to production fast, Azure Functions are a good choice that. It handles the underlying infrastructure and platform for your while you can solely focus on the business logic of your app. Due to that, you save a lot of time and money, especially when using the consumption plan where you only pay if your client uses the Function. This approach is an excellent alternative to other Web API frameworks if you don't want to think about the underlying platform.
When building REST APIs with Azure Functions, there are some things we should keep in mind:
Have you ever worked with Azure functions? What do you think about writing REST APIs with Azure Functions? Let me know in the comment section down below.