DEV Community

Ryan Levick for Microsoft Azure

Posted on

The Most Minimal Serverless Function

I've been having fun creating some APIs using Azure Functions. Functions as a Service (FaaS) is a category of 'serverless' products that allow me to write code and easily deploy it without having to worry about anything beyond whether the code itself works - provisioning servers, installing dependencies, and paying for idle servers are all a thing of the past.

Unfortunately, first getting started with Azure Functions wasn't as smooth as I would have hoped because I didn't fully grasp all the parts that make up the Azure Functions development flow.

In this post, we'll get a better understanding of how Azure Functions works by creating the simplest function we can by hand. We'll then discuss the various tools available to us that create this boilerplate for us so we don't have to. By the end, you should have a good idea of what's actually happening when you use these tools to help create Azure Functions.

Creating a Project

First thing we'll want to do is to create an Azure Function project on our local dev machine that contains all the functions for whatever project we're working on. This will have all the code for functions that we can check into source control and some configuration we can use for local testing of our functions.

At a minimum, a project simply has a host.json file which specifies which version of the runtime we want to use. This looks like this:

{ "version": "2.0" }

With this one file we have a working Azure Functions project! Of course, we have no functions, so it doesn't do too much. Let's create a function.

Creating a Function

Our function will be a node function. In the case of node, at a bare minimum we need to create a directory with whatever name we want for our function (e.g., "MyHttpFunction") that has a JavaScript file named index.js that has one exported function we'll look at below and a function.json file that describes the function.

To inform the functions runtime that we're expecting an incoming request and we want to provide an outgoing response we need to create a function.json describing these bindings (MyHttpFunction/function.json):

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "authLevel": "anonymous",
      "methods": [ "get" ],
      "name": "req"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

The first object in our bindings array says we want this function to be triggered by an http request (specified by "type" being set to "httpTrigger"), it will require anonymous authorization (a.k.a no authorization), and it will only be triggered on http GET requests. We also have to provide a "name" although we could call it whatever we want since it we access it as a parameter to our function. By specifying the "direction" as "in" we're saying that this is an incoming http request that's triggering our function.

The second object says we want to create a binding of type "http" that we'll set in our function on a key named "res" on our context object. By specifying the "direction" as "out", we're saying our function will return or output an http response.

Next, let's create the JavaScript file MyHttpFunction/index.js:

module.exports = async function (context, req) { // req is the request that triggered this function
  context.res = { body: "Hello" } // we specified res as our response object in our bindings
}

This function will simply ignore the incoming request and set the body of the response to "Hello".

While we're using node in this case, Functions work with many other languages as well. For some other languages like C#, just the code file is enough since these languages use attributes in the code to specify the information - no directory or function.json file is needed. If we were to create a minimal C# function it would probably look like this:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace MyFunctions
{
    public static class HttpTrigger
    {
        [FunctionName("MyHttpFunction")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
            ILogger log)
        {
            return (ActionResult)new OkObjectResult("Hello");
        }
    }
}

We can test our functions locally by installing the Azure Functions CLI tool (which we can easily install on macOs with brew brew tap azure/functions; brew install azure-functions-core-tools and on Windows with npm npm i -g azure-functions-core-tools --unsafe-perm true) and running the following:

func host start

Function Apps

So now we have a project and a function inside of it. We can run this function locally, but obviously we'd like to deploy this to the cloud.

All functions in Azure are contained within Function Apps. Function Apps are a way to group different functions to together into logical groups. If you're creating an API for instance, you'll probably have one function per endpoint and group all of those functions into one Function App.

Function apps require you to make decisions about a few things:

  • Where will your functions live? The obvious choice here is to pick a place nearest to your users.
  • Which OS the function is running on? Because code you're writing for functions is generally cross platform and you're not having to manage the infrastructure yourself, it shouldn't matter too much which platform you choose. You can edit Windows functions but not Linux ones from the portal (which we'll discuss later), but this isn't a huge advantage.
  • Which hosting plan should we use?: consumption plan means you'll only pay for whatever you use. If you have a large spike in demand though, your app may temporarily slow down as the Azure automatically provisions more capacity for your function.
  • Which runtime stack will we run on?: This needs to match whatever language runtime you've developed your function against. In our case, we'll want to select node.

Creating a Function App

There are several ways to create an Azure Function App, but perhaps the simplest way for now to create one is to use the Azure Portal.

We can create an Azure Function App by clicking on the "Create a resource" button in the top right of the portal, searching for "Azure Function" and following the wizard.

Publishing our Functions

Once our Function App is created, it's waiting for us to publish functions to it. We can do this using the Azure CLI tool and the Azure Functions CLI tool we used to test our functions locally. You can do this by running:

az login # logs you into Azure
func azure functionapp publish $NAME_OF_THE_FUNCTION_APP_YOU_CREATED

It's important to realize that this tool replaces the contents of the Azure Functions App with the content of your local Functions project. If you've already deployed functions to that Functions App, those will be overwritten. But the first time we run this, it will simply replace the empty contents of the Functions App with the contents of our local project.

Once this is done, you will have a running Azure Function!

Reducing the Boiler Plate

While we were easily able to create very simple functions manually, there are tools that will help us with all these steps so that we don't have to write out all the boilerplate ourselves.

Creating an Azure Function Project

Using the Azure Functions CLI tool, we can run func init to initialize a project. This will take you through some questions about which language runtime (e.g. node, dotnet, etc.) you want to use, and eventually it will create the necessary files for a project like the host.json file we've already seen and some other files that help you customize the functions environment.

If you prefer Visual Studio Code, you can use the Azure Functions extension. With that installed, you can open the command palette (cmd/ctrl+shift+p) and search for "Azure Functions: Create New Project" to create a new project. This will also take you through a wizard experience for creating a function similar to what you will find with func init. This command also allows you to create the first function (but if you want you can choose Skip for now...).

Creating an Azure Function

Using the Azure Functions CLI tool, you can run func new to take you through a wizard process for creating a function. You just need to think about what kind of trigger for your function you want.

For Visual Studio Code users who have the Azure Functions extension, you can use the command palette (cmd/ctrl+shift+p), search for "Azure Functions: Create Function". This will take you through a wizard experience similar to the one from the func CLI tool.

Additionally, you can create a function from the Azure Portal from the Functions App screen (there's a big plus button next to the list of functions). An issue with this is that you won't have any local copy of your function that you can save in version control. You'll want to eventually download the functions app your function is a part of to your local machine so you can add it to your version control system. This typically requires merging functions that exist locally with the one that was just created on Azure which can be a real headache...

You can also make edits to running functions, but you'll need to repeat those changes in your local project if you want it to be reflected in version control. My recommendation is to try to avoid creating or editing functions from the portal.

Creating a Function App in Azure

If you have the Azure CLI tool installed, you can run az functionapp create along with the required options. Pro tip: you can use the Azure CLI tool from the portal by using the Cloud Shell feature which gives you a bash or powershell command line with the tool already installed.

From Visual Studio Code, using the command palette (cmd/ctrl+shift+p), you can search for "Azure Functions: Create Function App in Azure". This will take you through a Wizard experience similar to the Azure CLI experience.

Conclusion

Overall, it's important to remember that functions don't require very much setup to run. You'll need to make sure you've created an Functions App and the minimal pieces for a local project. Each function requires at most 2 small files to run.

By now you should have an understanding of at a minimum what is required to run an Azure Function and the many ways that you can create those pieces. If you want to keep going, I recommend checking out the MS Learn learning path on Functions! If you want to learn more, please feel free to reach out to me on Twitter!

Top comments (0)