Carlton Upperdine
me enjoying the scenery of Zakynthos town

Serverless Deno with Azure Functions

Published: Sat Mar 04 2023

I've recently been delving into the world of Deno: a competitor to Node.js that boasts an easier and faster runtime experience for JavaScript. It is written using Rust and designed from the ground up for high performance, with aims to provide a secure, more productive experience for the modern developer.

So far, I'm really enjoying my experience with Deno- I'd probably describe it as a lot like Node.js but with a design philosophy more akin with Golang. For example: Deno ships with a formatter, linter and test runner out of the box, and has a standard library built in. This, combined with it's out-of-the-box TypeScript support, has me very excited.

Unfortunately, a lot of cloud platforms don't explicitly support Deno yet- so unless you are running in containers you'll struggle for first-class runtime support. Luckily, Azure Functions have an option for custom language bindings, which allows developers to write functions using whatever language or runtime they desire, including Deno!


Creating an Azure Function project

Firstly, we need to create a new directory to house our code in. I'll use the name deno-functions but feel free to call it whatever you like. From your terminal:

mkdir deno-functions && cd deno-functions

Now that we are in the deno-functions directory, we need to use our Azure Functions Core Tools to initialise our function project so that the Core Tools knows that we're dealing with Azure Functions. We do that using func init and specifying our desired runtime as custom:

func init --worker-runtime custom

This will create a few files, but the most important one for our project is host.json. This file tells our Azure Functions runtime how to run our functions.

Configuring our project to work with Deno

In order for VS Code to allow us to write code using the Deno standard library, we need to configure the project to enable the Deno plugin. If you already have the Deno plugin for VS Code installed, you can press F1 on your keyboard and select the option > Deno: Initialize Workspace Configuration. After doing this, you will be asked a couple of questions, and I would recommend the following answers:

  • Enable Deno Linting: Yes
  • Enable Deno unstable APIs: No

Alternatively, you can just create a settings.json file in the .vscode directory and include the following:

{ "deno.enable": true }

Creating our first function

Now we're all set up to work with Azure Functions and Deno, we're ready to create our first function! We do this using the func new command:

func new --name deno-http 
        --template "HTTP Trigger" 
        --authlevel anonymous

I have gone with the name of deno-http but feel free to call it whatever you want- just remember to swap in this name when I reference it later in the post. This creates a new directory called deno-http and inside it a function.json file:

{ "bindings": [ { "authLevel": "Anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ] }, { "type": "http", "direction": "out", "name": "res" } ] }

This file is used to declare the bindings for the function, along with the authentication method, direction and method. We don't need to touch this file any further, but if you'd like to, you can remove the post entry from methods as we won't need it for our use-case.

Now it's time to write our actual function code. Create a file in the deno-http directory called deno-http.ts. Again, the name doesn't matter here, just keep it in mind when it's referenced in future. Populate your file with the following code:

import { serve } from ""; const port = (Deno.env.get("FUNCTIONS_CUSTOMHANDLER_PORT") ?? 0) as number; const handler = (_req: Request): Response => { const body = JSON.stringify({ message: "Hello from Azure Functions with Deno!", }); return new Response(body, { status: 200, headers: { "content-type": "application/json" }, }); }; console.log(`HTTP webserver running. Access it at: http://localhost:${port}/`); await serve(handler, { port });

We have some important things to cover here.

Firstly, line 1 is now we import dependencies in Deno. If you've ever written any Go, this should look familiar. By referencing the url directly, we are able to know exactly where that code we are referencing is coming from.

Secondly, our code needs to be listening on the correct port. This is done on line 3 by setting the port to the FUNCTIONS_CUSTOMHANDLER_PORT environment variable provided by the Azure Functions runtime.

The rest of it should be fairly self-explanatory if you've ever seen a http server written with TypeScript, but the general gist is that our function returns a JSON object with the message of Hello from Azure Functions with Deno!

Compiling our code into an executable

In order to run our code as a Function, we need to provide our Deno function as an executable. We do this using the deno compile command and provide it with the path of the file we want to compile.

deno compile --allow-env 

Remember when I said earlier that Deno aims to be more secure than it's predecessor? Well when we are running or compiling our Deno code, we must specify if we want to allow our code to access environment variables or be open to the network. We do that using the --allow-env and --allow-net parameters.

We should now have an executable called deno-http in the root of our repository. We can run this executable just like any other web service and it will give us exactly what we need - try it for yourself!

Running our executable as an Azure Function

Now that we've got an executable for our application, we need to point our Function application to run it. We do that by modifying the host.json file. Open the file and replace the contents with the following:

{ "version": "2.0", "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[2.*, 3.0.0)" }, "customHandler": { "description": { "defaultExecutablePath": "deno-http" }, "enableForwardingHttpRequest": true } }

The most important part of this lies within the customHandler property. It specifies the path to the executable and allows our Function host to forward our HTTP requests to the correct port (this is incredibly important).

Once you have done that, open up your terminal and enter the following command:

func start

Doing so will start up our Function host and inform us where our functions are accessible. When I do this on my machine, I get the following

Azure Functions Core Tools
Core Tools Version:       4.0.4426 Commit hash: N/A  (64-bit)
Function Runtime Version:

[2023-03-04T18:56:46.880Z] Listening on http://localhost:59769/


        deno-http: [GET,POST] http://localhost:7071/api/deno-http

For detailed output, run func with --verbose flag.
[2023-03-04T18:56:47.331Z] Worker process started and initialized.

I can now access my function using the web browser or curl, and if this works, you should receive the following:

{ "message": "Hello from Azure Functions with Deno!" }

Deploying to Azure

Providing you have your Azure account set up properly, you can then deploy your function using the following command:

func azure functionapp publish deno-http

Alternatively, you can use the Azure Functions extension in VS Code to deploy your code if you prefer.

I appreciate that there are a lot of different ways of deploying Azure Functions, so I'm avoiding the topic of CI in the aim of not expanding the scope of this post too far.

Final Thoughts

I have written a lot of serverless functions in my career, but being able to use custom languages and runtimes may just change how we write them going forward.

I understand that Go and Rust are frequently used with Azure Functions in this context, but the possibilities for language are endless.

For example, you are able to write functions with scientific languages like R, which probably has a lot of use-cases in the data science world.

Deno is doing a lot of things right, and it makes me happy that I can pick it up so quickly and build something like an Azure Function with minimal overhead.

Also worth a mention is that Deno has it's own serverless platform to run on called Deno Deploy that apparently requires even less set up than Azure Functions. I can't speak from personal experience as I haven't used it myself, but based on my experience so far with Deno I may do soon!

You can find the source code for this blog post here.

Thanks for reading!