Azure Function Proxies – Part 1: Represent heterogenous service operations into a single API

This post is part of an Azure Function Proxies blog series:

  • Part 1: Represent heterogenous service operations into a single API
  • Part 2: Easily enable hybrid integration
  • Part 3: Secure your API
  • Part 4: A very light-weight API management

When using the Microsoft Azure offering, an important aspect is to use the right service for the job.  In this way, you might end up with an API that is composed of several Azure services such as Blob Storage, Logic Apps and Azure Functions.  In order to publish such an API in a unified way, you can leverage Azure Functions Proxies.

Scenario

In this fictitious scenario, I’m building a Pizza Delivery API.  It’s composed of the following operations:

  • GET Products is still in development, so I want to return a mocked response.
  • GET Product returns the product information from Blob Storage.
  • POST Order invokes a Logic Apps that creates the order in an async fashion.
  • GET Order call an Azure Function that retrieves the specific order information
  • PUT Order is responsible to asynchronously update an order via Logic Apps
  • DELETE Order removes the order from within a Logic App

Proxies - 01

In this blog, I’ll show you how to build this API, without writing a single line of code.

Walkthrough

Introduction

This section just demonstrates the basic principles to start creating your first Azure Function Proxy.

  • Azure Function Proxies is still in preview.  Therefore, you need to explicitly enable it on the Settings tab.

Proxies - 02

  • Now, you can create a new proxy, by using the “+” sign.  You need to specify a name, a route template, the allowed HTTP methods and a backend URI.  Click Create to save your changes.

Proxies - 03

  • When the proxy is created, you get immediately the proxy URL on which you can invoke the Azure Function Proxy.  Via the Advanced editor link, you can define more options in a JSON format.

Proxies - 04

  • The advanced editor is a JSON editor of the proxies.json file.

Proxies - 05

After this introduction, we can start the implementation of the Pizza Delivery API.

Mocking

This operation won’t have a backend service, because it’s still in development.  We will mock a response, so the API can already be used for development purposes.

  • Create a proxy GetProductsViaMocking.  Provide the following details:
    • Route template: api/products
    • Allowed HTTP methods: GET
    • Backend URL: none, we will use mocking

Proxies - 06

  • In the Advanced editor, specify the following responseOverrides.  This sets the Content-Type to application/json and the response body to a fixed list of pizzas.
"responseOverrides": {
   "response.headers.Content-Type": "application/json",
   "response.body": "[{\"Id\": 1, \"Name\": \"Pizza Margherita\", \"Description\":  \"Excellent!\"},{\"Id\": 2, \"Name\": \"Pizza Quattro Formaggio\", \"Description\":  \"Delicious!\"},{\"Id\": 3, \"Name\": \"Pizza Funghi\", \"Description\":  \"Fantastic!\"}]"
}

Untitled picture

  • If you test this GET operation, you’ll receive the mocked list of three pizzas!

Proxies - 07

Blob Storage

The GET Product operation will return a JSON file from Blob Storage in a dynamic fashion.  Blob Storage is an easy and cheap way to return static or less frequently changing content.

  • Create a blob container, named “pizza”.  Set its access level to private, so it’s not publicly accessible by default.  Add for every available pizza, a JSON file that holds the product information.

Proxies - 08

  • Create an Access Policy on container level.  The policy provides read access, without any expiration.  You can easily do this via the Azure Storage Explorer.

Proxies - 09

  • Create a Share Access Signature from this policy, via the Azure Storage Explorer.  This gives you a URL (containing the SAS token), to access the blobs from the “pizza” container.

Proxies - 10

  • To keep the SAS token better manageable, I prefer to add the SAS query string to the Application Settings of our Function App.  Create an Application Setting BLOB_SAS_QUERYSTRING.

Proxies - 11

Remark that the route template parameter ‘productId’ is reused in the Backend URL to get to correct JSON file.  Remark that the application settings can be retrieved via this %BLOB_SAS_QUERYSTRING% syntax

Proxies - 12

  • In the Advanced editor, specify the following responseOverrides.  This sets the Content-Type to application/json
"responseOverrides": {
   "response.headers.Content-Type": "application/json"
}

Proxies - 13

  • If you test this GET operation, you get the product description of the corresponding product id.

Proxies - 14

Logic Apps

The asynchronous order operation will be tackled by a Logic App that puts the commands on a ServiceBus queue.  Further downstream processing is not covered in this post.

  • Create a Logic App as shown below.  Remark that the request trigger contains an {operation} parameter in the relative path.  The request command is sent to an orderqueue, with the {operation} as a message property.

Proxies - 15

  • Get the request URL from the Logic App.  This contains also a quite long query string, that includes the API version and the SAS token.  Similar to the previous step, insert the query string into an Application Setting named LOGICAPPS_SAS_QUERYSTRING.

Proxies - 16

Remark that the create operation is passed via the Backend URL

Proxies - 17

  • If you test this POST operation, you’ll notice that the message is accepted.

Proxies - 18

  • In the Logic Apps Run History, you should see that a new Logic App instance got fired.

Proxies - 19

  • The message is now placed on the Service Bus queue.  The operation has been set to create.

Proxies - 20

  • Create also a proxy for the PUT and DELETE operation, in a similar way.

Proxies - 21

Azure Functions

  • I’ve created an Azure Function that just returns a dummy order.  The returned order ID is taken from the request.
#r "Newtonsoft.Json"
using System;
using System.Net;
using Newtonsoft.Json;
using System.Collections.Generic;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage request, int orderId, TraceWriter log)
{
    log.Info($"Webhook was triggered!");

    var order = new Order();
    order.Id = orderId.ToString();
    order.Name = "Toon Vanhoutte";
    order.OrderLines.Add(new OrderLine(1, 3));
    order.OrderLines.Add(new OrderLine(3, 2));

    return request.CreateResponse(HttpStatusCode.OK, order);
}

public class Order
{
    public Order()
    {
        OrderLines = new List<OrderLine>();
    }

    public string Id { get; set; }
    public string Name { get; set; }
    public List<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    public OrderLine(int productId, int quantity)
    {
        ProductId = productId;
        Quantity = quantity;
    }

    public int ProductId { get; set; }
    public int Quantity { get; set; }
}
  • Deploy this Azure Function in a Function App and retrieve the URL.  The URL contains a query string with the required access code.  Add this query string to the Application Settings, named FUNCTIONS_CODE_QUERYSTRING.

Proxies - 22

Remark that the route template parameter ‘orderId’ is reused in the Backend URL to pass it on to the Azure Function.

Proxies - 23

  • If you test this GET operation, you’ll notice that a dummy order is returned, with the requested Id.

Proxies - 24

Feedback to the product team

After playing around with Azure Function Proxies, I have the following suggestions for the product team.  They are listed in a prioritized order (most wanted feature requests first):

  • The mocking experience is not ideal when working with JSON objects, as you need to constantly escape quote characters.  Would be great if this could be made more user-friendly.  As an alternative, you can reach out to blob storage, but this additional step decreases developer productivity.
  • It would be nice if there is a way to remove all or some HTTP headers from the response.  Each Azure service comes with its own set of HTTP response headers and I don’t want them to be returned always to the client application.  This would be great from both a security and consistency perspective.
  • Accessing parts of the request or response body would open up a lot of new opportunities.  As an example, I’ve tried to put the message immediately on an Azure Storage Queue, without any code in between.  However, this was not feasible, because I needed to wrap the original message content inside this XML template:
<QueueMessage> 
  <MessageText>OriginalMessageContent</MessageText> 
</QueueMessage>
  • Currently there is no way to perform different transformations, based on – for example – the backend response status code.  This could become handy in certain scenarios.

Conclusion

Azure Function Proxies can be easily used to create an API that consists of multiple HTTP-based (micro)services.  Practically every service can be accessed from within this light-weight application proxy, as long as the authentication is performed through the URL (e.g. SAS token, API key).  This avoids that the client application needs to maintain a different URL per operation it wants to invoke.  The service is very easy to use and requires no coding at all.  A great addition to our integration toolbelt!

ABOUT

MEET THE YOUR AZURE COACH TEAM

Your Azure Coach is specialized in organizing Azure trainings that are infused with real-life experience. All our coaches are active consultants, who are very passionate and who love to share their Azure expertise with you.