Menu Home

Using TypeScript in Azure Functions (and with Visual Studio Code)

I really like TypeScript and now use it in most projects that call for JavaScript, whether it’s for frontend code in a web browser or backend code run by Node.js. The pros and cons of TypeScript compared to JavaScript are well documented but for me the clincher is the excellent IntelliSense in Visual Studio Code (and other dev environments). While it’s early days for TypeScript support in Azure Functions it is relatively easy to use and provides a fantastic local development experience.

In the context of Azure functions, another advantage of TypeScript is that we can escape the limitations of Node 6.5.0 currently supported by Functions and use newer JavaScript features such as object rest and spread (perfect for use in functional style code). The TypeScript ‘compiler’ transpiles down to a specified earlier ‘version’ of JavaScript as well as stripping out the type information.

Use of TypeScript in Functions requires both running the tsc transpiler and accessing the Typings that describe the Functions’ run-time environment types, for example, the ‘context’ parameter and HTTP return code names.

Transpiling to Javascript

The are 2 approaches to transpiling TypeScript with Functions:

1) Run the TypeScript compiler as part of the local build process and deploy the generated JavaScript to Azure Functions as usual. It doesn’t matter if extra files like the TS source and map are also uploaded as they will be ignored by the Functions runtime. Note that it’s not clear from Node.js documentation exactly what features of Javascript a given version of Node.js supports. Thus, a little trial and error could be required in setting the tsc ‘target’ and ‘lib’ options.

2) Use ts-node and deploy the .ts files to the cloud FunctionApp. ts-node will intercept run time calls to require() and run the TypeScript compiler to create the JavaScript. The problem with this is that Node.js Functions cold start time is already very slow and this will just make it worse. I’ve not tried this at all so the interactions with Function Web UI and run time have not been tested. In fact, the web UI makes assumptions about files so there are bound to be issues.

The tsc config for use on the development environment is pretty straight forward (see tsconfig.json below). We simply compile the index.ts in each function directory. You can easily add other TypeScript modules and libraries. These can per function, or more likely, be shared by all the Functions in the Function App taking advantage of the way modules are resolved by searching ‘node_modules’ up the directory structure.

In addition to setting the tsconfig.json for Node.js and the version we are using, we also configure it to get any helper injected functions from ‘tslib’. These small helper functions are by default in-lined in the generated JavaScript but if there are a lot of them using a shared copy creates less code (though that is unlikely to be a big issue).

Azure Functions’ run-time Typings

The Functions’ run-time Typings are available in the unofficial npm package azure-functions-typescript. The package is installed as a dependency and required types imported in the TS source (see example below). A sample Function is also provided in this package which you can use as a template for your own TypeScript Functions.

Note that currently, this package uses an old version of Typescript and the now depreciated Typings npm package. That should not matter for consuming the typings but you might want to look at the updated sample Function in this PR.

Local Development with VSCode

Local development of TypeScript functions on Node.js is a fantastic experience with Visual Studio Code in combination with Azure Functions Core Tools. This local development capability is unique to Azure Functions amongst the Serverless providers.  Currently however, this is limited to Windows development (but not Unbuntu on Windows) due to a limitation of the core tools. This restriction is being worked on right now.

You can easily run the tsc compilation and use the debugger. You can set breakpoints and watches in the TypeScript source files when mapfiles are generated and seen by the debugger. Any npm scripts defined in the ‘package.json’ can be run from the command line as usual (including in code’s integrated terminal) or executed directly in code using it’s task runner features. You can also debug the generated JavaScript used by the Functions runtime. This is described in the Azure Functions Documentation on Local Development and the code debugging documentation.

Code stopped at a breakpoint in a local TypeScript Function

Remember to use node 6.5.0 locally to match the Functions runtime and avoid any surprises. You might want to use nvm-windows to manage Node.js versions and easily switch between them.

You may get excited to see that Azure Functions Core Tools offers TypeScript as a language option when interactively creating a function. Currently however, it only creates a faux TypeScript function with the input parameters typed as ‘any’. It does nothing to run tsc and does not include the Typings (yet). You’ll want to install the azure-functions-typescript package and set up the build as described above.

Deploying and Live Debugging

Once you have the transpiled JavaScript you can deploy it using any of the usual methods that Azure Functions or the Core Tools provide, possibly as part of a build or CI / CD process. You might want to use the webpack bundler package to create a single JavaScript file including both your Functions and required packages from node_modules, thus speeding up cold starts.

However, when it comes to editing or debugging in the Functions cloud Web UI you only have access to the Javascript not the TypeScript source. It might be possible to upload the TypeScript source with map files and figure out how to get the web UI to us it in errors and the editor (see improvements below). Hopefully the functions team will do this soon.

That said, the range of code transformations that tsc performs are limited, especially if you get the TS config options for ‘target’ and ‘lib’ properly matching the version of NodeJs being used. Thus, debugging the JavaScript is not so difficult to manage, manually mapping back to the TypeScript source.

Of course, live debugging is a general weakness of Serverless in and micro services and you’ll no doubt be using logging and/or App Insights to track what’s going on :).

Details

Here’s an outline of the process you could follow.

  • Create a function with Azure Functions Core Tools and use something like the following files (for an HTTPTrigger example)
  • Test and debug the function locally with VSCode, possibly connected to live Azure resources such as queues (so cool)
  • Deploy and test.

package.json

tsconfig.json

<Function>/index.ts

[UPDATE: 2017-08-20] NB the export format export = is important. It sets the commonjs module.exports variable to be the function. The is especially important when using the ‘azure-functions-pack’ to bundle the code for fast upload and cold starts. If you don’t use this form then function run time will not find the Function entry point, giveing a 500 error form Azure or a more specific error in local development.

Ideas for Improvements

A minor disadvantage with this simplistic setup is that the generated Function ‘index.js’ and map are located in the same folder as the ‘index.ts’. This means the Function runtime will  correctly locate ‘index.js’ but makes it more complex to not check in the generated files into version control. An improvement would be to config TS to place the outputs in a separate folder (say dist) and configure the Functions runtime to access the ‘index.js’ from there.

Another possible improvement is to extend the types so that expected query items such as ‘req.query.name’ are known.

Categories: Uncategorized

Tagged as:

steve@opendirective.com

1 reply

Leave a Reply