Azure Functions ⚡ A Developers Guide to Key Features
Dean Bryen
Posted on March 15, 2019
How to get started with Azure functions
The past year of my life as a developer has been focused on diving deep into Azure Functions. Now that I’ve had the chance to come up for air, I wanted to share a few of my lessons to help other developers on their serverless journey.
I’ve identified a few of my favorite features that has made developing with Functions as a Service (FaaS) a lot easier. Please hit me up in the comments with your feedback — I’d love to hear what features would help improve the next year of your life as a serverless developer.
Simple & Native Developer Tooling
While I’m a huge fan of the Serverless framework, their Azure support isn’t fantastic. The framework currently only supports Functions and it’s done via a plugin, which makes it feel a little like a second-class citizen. While I have high hopes for their components project — that’s for another blog post.
At first, I was crying out for an equivalent framework for Azure — and there still might be an argument for this approach. But as I dug deeper into functions, I’ve become the #1 fan of Visual Studio Code.
With VScode, I’ve found it pretty seamless to get done everything from within the tool. This comes down to a couple of things:
VSCode Extensions and Functions v2.0 Local Runtime
Anyone who has used VSCode will be familiar with the extension ecosystem — and the the functions extension for VSCode is solid. The extension relies on azure-functions-core-tools
which is a simple command line tool, so you can run the Azure Functions runtime locally. As an added bonus, the CLI can also be used independently of the VSCode extension.
The v2.0 runtime of Azure Functions is open source and runs on .NET Core. This means that what you install on your local machine — whether Mac, Linux or Windows — is the exact same runtime as you get in the Azure Cloud. No emulators or simulators trying to mimic the behavior of the cloud provider.
Using a combination of the local runtime and the extension, you can create a function project, add some functions, test and debug them locally, and then publish them to Azure — within just a few clicks or commands. Once it’s running, you can simply stream the logs directly from the function running in Azure to your terminal in VSCode.
Function Bindings
Sadly, when people talk about serverless it often centers around Functions as a Service (you mean a bit like this blog post, Dean? 😛).
FaaS is just one piece of a serverless application or architecture. If you really want to make any application that’s worth talking about, it’s likely that your Functions will need to integrate and communicate with a ton of other services and systems. It could be as simple as accessing a storage container or writing to a database — either way, you can’t achieve much with just FaaS.
Azure Function Bindings offers developers a simple way to integrate with a bunch of services. This is great for a couple of reasons. First, changing the database no longer requires developers to edit our business logic that sits within our function code — we can just update the binding configuration. Secondly, it just makes your code much simpler.
Bindings come in two flavors
- Input — these happen before your function code is executed
- Output — these happen upon completion of your function code.
There’s a vast number of bindings supported, and I use quite a few on a regular basis. To get started, here’s a quick and easy example that demonstrates how to use the CosmosDB bindings.
One of the really nice features is that ability to configure our bindings right alongside our function code in a function.json
file. Here’s our input binding to fetch a document from our CosmosDB collection via it’s id
.
{
"name": "cosmosDBInput",
"type": "cosmosDB",
"databaseName": "MyDatabase",
"collectionName": "MyCollection",
"id" : "{id}",
"connectionStringSetting": "myCosmosDBAccount",
"direction": "in"
}
To interact with the binding, we just reference it by name with some super simple code below. I’m using Javascript, but any of the supported languages can achieve the same.
module.exports = function (context) {
let data = context.bindings.cosmosDBInput;
context.log(data);
context.done();
};
We can also do something very similar for an output binding. Below is how you could write a single document to a cosmosDB collection.
First, we need the binding configuration in function.json
{
"name": "puppyData",
"type": "cosmosDB",
"databaseName": "MyDatabase",
"collectionName": "MyCollection",
"createIfNotExists": true,
"connectionStringSetting": "myCosmosDBAccount",
"direction": "out"
}
Next, we can simply use that binding within our function code:
module.exports = function (context) {
context.bindings.puppyData = {
id: 1,
name: "Rover",
breed: "Great Dane",
address: "Big Dawg House, Paw Lane, London"
};
context.done();
};
If you’re using Functions, you’ll want to use Bindings .
Built in HTTP Trigger
After working with other cloud providers, it was always necessary to spin up some form of an API Gateway in addition to my function so I could serve web requests over HTTP.
I’m all for API Management and Gateway services — they can bring a lot of value to your APIs. But for some use cases they’re a little bit overkill, especially if you just want to invoke the function over HTTP natively.
With Azure Functions, this feature comes out of the box. You just need to provide your function a HTTP Trigger, and then you can invoke it straight from the web.
Don’t panic — this doesn’t mean just anyone and everyone can now invoke your function. You have the ability to select one of three levels of authorization for the HTTP Invocation: anonymous
, function
or admin
. The latter two requires a key to invoke the function via the web.
Just like bindings, we can have our HTTP trigger’s configuration alongside our code in the function.json
file — and it looks just like bindings. Here’s an example of what your file may look like:
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
Now you can simply reference the HTTP Trigger in your code via it’s name req
. One interesting thing to note — the HTTP response is actually just using the http
output binding.
Function Proxies
While the HTTP trigger gives you a simple invocation over the web, what if you need something a little more than a HTTP Trigger — but a bit less than a full blown API Gateway?
Function Proxies provide developers the ability to have a lightweight API gateway — right within your Function App. Similar to both triggers and bindings, the configuration resides alongside your code in a proxies.json
file. In it’s simplest form, a function proxy configuration will look something like this:
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": ["GET"],
"route": "/api/{test}"
},
"backendUri": "https://<AnotherApp>.azurewebsites.net/api/<FunctionName>"
}
}
}
In this example, we are simply routing our proxy to a function as the backend. Function Proxies also support things such as request and response overrides, so you can manipulate the HTTP requests in the same manner as most enterprise API gateways.
Function Proxies are rally handy for a couple of things, especially 1) avoiding issues with cross-origin resource sharing (CORS), and 2) mocking out your API responses.
Avoiding CORS issues
I build a lot of Single Page Apps (SPAs) that I host in Azure Blob Storage. Whenever I connect them to my functions, I usually encounter a CORS error since they’re hosted on different domains.
There’s a couple of ways to fix this. One option is to update the CORS settings in your Function App to accept the storage container’s domain. Or … you can simply use Function Proxies for both your Functions and your Azure Blob — so you’re basically putting them on the same domain. No more CORS errors. TADA!
To use Function Proxies to avoid CORS issues, just put the URI of your storage container as the backendUri
of your proxy configuration. Here’s an example:
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": ["GET"],
"route": "/"
},
"backendUri": "https://myserverlessapp.z6.web.core.windows.net/index.html"
},
"proxy2": {
"matchCondition": {
"methods": ["GET"],
"route": "/media/{*restOfPath}"
},
"backendUri": "https://myserverlessapp.z6.web.core.windows.net/media/{*restOfPath}"
}
}
}
Mocking out API responses
Azure Functions Proxies also provides a simple way to mock responses for your API. You can basically create a Function Proxy, don’t give it a backend URI, and then you use the requestOveride
feature to return your mocked response. Over to you front end dev! Here’s an example:
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": ["GET"],
"route": "/api/hello"
},
"requestOveride" : {
"response.statusCode": "200",
"response.body": "{ \"message\": \"Hey There\" }"
}
}
}
}
What’s your thoughts?
I’m hoping you’ve enjoyed learning about these features — they’ve certainly made my experience as a developer using Azure Functions more enjoyable.
What features are making life easier for you? What features are on your wish list for Azure Functions? I’d love to hear from you in the comments below, or connect with me on Twitter at @deanbryen.
Posted on March 15, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.