Serverless Azure Architecture: Node.js, Azure Functions and API Management

Have I told You that I really enjoy playing with Node.js ?

So I needed to demo some Azure API Management and I decided to write the functions stubs with Node.js. Easy enough when using Azure Functions, just create new Function and specify that You want to use Node.js instead of C#.  BUT WAIT ... there's always a BUT ... This time there are three buts actually.

First hurdle

Since Azure Functions is designed to be simple , it is ... well simple.  If You want to provide several pieces of functionality , like You would in a typical REST CRUD situation, You would have to write several separate disconnected functions or use some sort of dispatcher inside one function the keep the working code in one place. This time I chose to use a dispatcher :

2016_06_code
I've agreed with myself that there has to be a parameter called "op" which decides the functionality to be served, other parameters are then related to the contents of the actual service. Not maybe the best thing in the world but doable , understandable and simple enough even for maintaining after one year.

Second hurdle

When making stuff like this there is no automagically produced swagger file to describe the functionality to API Manager, and Api Management needs it, not badly, but things are definitely easier if You have your swagger-description. So I decided write one by hand and emit it when my function is approached with parameter "swag=true".

Swagger is quite verbose so my swagger definition occupies many lines ... many many ... many lines. The best way (that I know of) to deal with multiline strings in javascript-environment is to use the backticks  to contain them. Like this:
var multiline = 'this
is
my multiline
string';
2016_06_swag
as I mentioned, the swagger string is quite lengthy and it won't fit in one screenful but it is included in the resources section.

Third Hurdle

After importing the Api's swagger-definition to Azure APi Management we have something that almost works, but not quite. We are missing some important pieces of information like the keys to use when accessing Azure Functions and the variable required by the dispatcher naming-scheme (remember, op ?). So I have to edit the automatically generated function signatures slightly by manually adding rewrite-rules where I add the code-variable which contains the key that allows Api Management to call my function and a reasonable value for "op" :
2016_06_apim_func
Now the Api Management is able to call my function and distinguish between the services that I expose and by managing the keys inside Api Management I get an extra layer of security as a bonus.
[sidetrack: If You need to make sure that your Function is not being called from internet You can choose to put it inside an App Service Environment which can have a totally isolated virtual network that has vpn-connection to Your other networks like on-prem or so]

Testing

I'm using the API Managements Developer Portal and tools found there to call my API. APIM automatically creates input boxes for all ingoing parameters:
2016_06_apim_dev
and here's what the call produced to Azure Functions log once executed:
RequestUri=https://xxxxxxx.azurewebsites.net/api/swagtest2?id=testblog&code=gncemex906oujh&op=findblogById
You called :findblogById
{"id":"testblog","code":"gncemex906oujh","op":"findblogById"}
Function completed (Success, Id=a8eb2ccc-8d44-40eb-9f95-444ac7951b8b)

Seems to be working just fine.

Conclusion

By using really simple tools I was able to create, manage and publish real enterprise API's and I did it before lunch. Now I can play with Autoscale settings and when I later on decide to change my implementation to live inside a real Api App or start using Scala or something cool I can just redefine the API Management definition to point to the new implementation without any other implications to the existing clients of my API.

Not that I needed to reimplement, Azure Functions scales just fine, which will be a topic for another future post. See Ya.

Resources

2016_06_function_code