Using Azure Functions as a lightweight API Gateway


Not sure if everybody saw the announcement a couple weeks back, but Azure Functions now allows you to create proxies:
https://blogs.msdn.microsoft.com/appserviceteam/2017/02/22/azure-functions-proxies-public-preview/

While you might think at first glance "what's all the hubbub about?" this feature is actually quite nifty. It allows you to establish microservice patterns by breaking up Functions, and expose a unified endpoint to the client side. Which should be enough by itself to consider using it, but it also allows you to do other useful things like building an API Gateway implementation which can come in handy both for prototyping and production purposes.

Will this be the end of Azure API Management or Apigee? No, not by a longshot. Those products have features for actually managing APIs, and doing things like access control, throttling, and a whole lot more. It's just that sometimes this is overkill for what you actually need in a given developer situation.

When I build my own APIs I might have a prototype client to call into them for basic verification while I work at the back-end. I might do the old approach of commenting out code while writing new test code to test a different approach, but inevitably I end up with multiple endpoints as I work along different paths before deciding which one will be the best. This is kind of a hassle since this means I have to do minor edits in the client just to call into a different endpoint, which feels sort of pointless. (Whether I change a string in the code and recompile, or edit a textbox in the UI it still counts as an edit.) With the proxy functionality in Azure Functions you can expose one endpoint to the client, and just juggle the actual API being used in this proxy.

Creating API endpoints

First thing we need is to expose some APIs. These will be dead simple, and nothing fancy. To do this we also use Azure Functions.

Create a new Function app in the portal, and go to the Integrate tab.

function_api01_01

As you can see I have selected GET as the verb here, and nothing else.

I went with the following code on the Develop tab:

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Set name to query string or body data
    name = name ?? data?.name;

    return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "GET: Hello " + name);
}

You can use the Test tool on the right hand side if you would like to make sure you didn't mess up something:

function_api01_02

So far so good. I create a second Function App this time with the POST verb allowed:

function_api02_01

Could I not have placed these in one Function App, as two separate functions? Yes, I could have, but illustrating the proxy concept works better this way in my opinion.

The code for this Function can actually be the same as the previous snippet. (Well, I changed the response to prefix the greeting with POST instead of GET to know which back-end returned the result.)

Verifying the endpoints

Just to make sure things are good so far we use Fiddler to verify we get the responses we want.

HTTP GET

fiddlerget_01

HTTP POST
Remember to include a body for this request (and if you get an error 500 it's because you forgot to include Content-Type: application/json as a header) :

fiddlerpost_01

Hopefully the response will be fairly similar to the GET.

fiddlerpost_02

Setting up the proxy

There's not that many steps to this really. You need to enable proxies under Function app settings. And create two new proxies like this:

GET - Proxy

functionproxy_get_01

POST - Proxy

functionproxy_post_01

And then you can change things around in your Fiddler client the way you want it to. The "gateway endpoint" stays the same, and as you modify which back-end endpoint serves the request you just edit the proxy parameters accordingly.

Wrapping it up

Well, this is nice and dandy, but what prevents me from skipping the proxy, and hitting the back-end directly? Nothing right now. What you can do as a workaround is to have authentication configured on the back-end requiring a code in the query string, and either go with a different code on the proxy or anonymous for that matter.

What if I have more creative needs like modifying the inbound request before making an outbound request from the proxy to the backend? Well, this isn't the final version of the proxy feature. I am not able to comment on the future, but expect more tweaks and settings to be added.

Do I have to go through the UI to work with the proxy setup? No, you can access the file directly by opening up proxies.json under site/wwwroot, and edit things. (Kudu works nicely for this purpose.)

 
{
    "proxies": {
        "GET": {
            "matchCondition": {
                "route": "/apigw",
                "methods": [
                    "GET"
                ]
            },
            "backendUri": "https://api01.azurewebsites.net/api/HttpGET"
        },
        "POST": {
            "matchCondition": {
                "route": "/apigw",
                "methods": [
                    "POST"
                ]
            },
            "backendUri": "https://api02.azurewebsites.net/api/HttpPOST"
        }
    }
}

Having an API Gateway isn't of much use if we don't do more exciting things than Fiddler on the client side, but what we can do on the other end of the wire is a story for another time. (Read: I'm working on that. After some consideration I deemed it to make sense to do the server part first though.)

sharp

Comments (7)

    1. And an important value prop of Azure is exactly how it allows one to be creative 🙂

  1. Pete Laker says:

    What an awesome post, thanks Andreas! The potential is STRONG with this one!

  2. Sjoukje Zaal says:

    Great new feature and a good post, Andreas!

  3. chilberto says:

    Thanks Andreas for the clear post; it has really made me think especially around versioning and environments.

  4. Dasiths says:

    Whats the best practice when consuming the function? Is it ok for a client app (angular app) to directly call the "serverless" function? Or does it have to communicate via a gateway (custom api, azure api manager, proxy)?

    1. There isn't a single answer to this, and it would depend on your general setup/architecture. There is nothing inherently wrong with consuming a Function directly from an Angular app, but it might be some advantages to have a layer in between.
      - Is the API only consumed by an internal app, or will it be offered to third-parties as well?
      - Are there considerations around latency and throttling of traffic?
      - Does authentication take place in the Function/API or the app? (This wouldn't necessarily be solved by this lightweight gateway either.)
      - Will the back-end be split across different fqdns, and served from different places while still wanting to expose one endpoint externally?
      - What is the versioning strategy? Will you change the code client side to move from api/v1 to api/v2, or do you want to just flick a switch server side?

      These are just a few considerations one needs to think about. So if you are on top of what you're trying to do it's entirely ok for an Angular app to talk directly to the serverless Function.

Skip to main content