In this module, you'll create a simple Function which listens for HTTP Requests and responds with an ASCII art response. We'll do this all locally to show how fast it is to develop and test locally. It is possible to do this all in the Azure Functions portal, however.
You'll need:
- Node 8.5.0
- Azure Functions Core Tools (@core)
- npm i -g azure-functions-core-tools@core
- This has a dependency on dotnet core being installed
- VS Code or similar text editor
- cURL, Postman, or a general REST API tool
Azure Functions can run locally with a very simple project structure. Essentially, you can create a directory which contains a child directory for each Function. It can also contain shared code/dependencies/static content. Each Function directory needs a function.json
in it in order to be discovered by the Functions runtime; this file specifies the behavior of your application. The Function directory should also contain your code or you need to add a setting to your function.json
on where that code lives. For most of this workshop, we'll just drop an index.js
file in the same directory and let the runtime discover it automatically.
Here's a simple ASCII representation of a Functions project structure.
(root)
- host.json
- local.settings.json
- package.json
- node_modules
-- (...)
- foo // <--- Function directory
-- function.json
-- index.js
- bar // <--- Function directory
-- function.json
-- index.js
Fortunately, you don't have to create this all by hand. We can us the Azure Functions core tools to template for us. To create a new Functions Project, let's create a new directory and initialize it.
# Create a new directory
mkdir hello-functions
cd hello-functions
# Initialize that directory
func init
It should show an output like so:
Writing .gitignore
Writing host.json
Writing local.settings.json
Created launch.json
Initialized empty Git repository in /Users/chris/workspace/hello-functions/.git/
The tool won't overwrite any existing files, so if you ever accidentally delete a file and want to recreate it (like if you don't check in your .vscode
directory), just run func init
again.
To create our first Function of the workshop, all we need to do is run:
func new
which will prompt us for which type of Function we'd like to create. We can also specify it via command line arguments. In this case, we want to create a JavaScript HTTP Function, so we'll run instead:
func new -l JavaScript -t HttpTrigger -n hello
which should output something like
Select a language: JavaScript
Select a template: HttpTrigger
Function name: [HttpTriggerJS] Writing /Users/chris/workspace/hello-functions/hello/index.js
Writing /Users/chris/workspace/hello-functions/hello/sample.dat
Writing /Users/chris/workspace/hello-functions/hello/function.json
In addition to the directory, this created three files for us:
index.js
which contains our codefunction.json
which contains our config for the Functionsample.dat
which is some test data you can use with the template out of the box. We don't need this, so you can delete it if you'd like.
Let's go ahead and test out our Function now.
To start your Functions, be sure you're in the root of your Function project and run:
func host start
This should print an output like this:
%%%%%%
%%%%%%
@ %%%%%% @
@@ %%%%%% @@
@@@ %%%%%%%%%%% @@@
@@ %%%%%%%%%% @@
@@ %%%% @@
@@ %%% @@
@@ %% @@
%%
%
[10/7/17 3:14:29 PM] Reading host configuration file '/Users/chris/workspace/hello-functions/host.json'
[10/7/17 3:14:29 PM] Host configuration file read:
[10/7/17 3:14:29 PM] { }
info: Worker.Node.d8612901-590c-4313-9a02-02a7d424f334[0]
Start Process: node --inspect=5858 "/Users/chris/.azurefunctions/bin/workers/Node/dist/src/nodejsWorker.js" --host 127.0.0.1 --port 60505 --workerId d8612901-590c-4313-9a02-02a7d424f334 --requestId 7e03b625-8175-41f7-a47b-f06dec532484
info: Worker.Node.d8612901-590c-4313-9a02-02a7d424f334[0]
Debugger listening on ws://127.0.0.1:5858/3ed53bc1-e73e-450e-b98b-d1d78b73c0ed
info: Worker.Node.d8612901-590c-4313-9a02-02a7d424f334[0]
For help see https://nodejs.org/en/docs/inspector
[10/7/17 3:14:30 PM] Generating 1 job function(s)
[10/7/17 3:14:30 PM] Starting Host (HostId=christophersmacbookpro-114832657, Version=2.0.11308.0, ProcessId=50327, Debug=False, Attempt=0)
[10/7/17 3:14:30 PM] Found the following functions:
[10/7/17 3:14:30 PM] Host.Functions.hello
[10/7/17 3:14:30 PM]
[10/7/17 3:14:30 PM] Job host started
info: Worker.Node.d8612901-590c-4313-9a02-02a7d424f334[0]
Worker d8612901-590c-4313-9a02-02a7d424f334 connecting on 127.0.0.1:60505
Listening on http://localhost:7071/
Hit CTRL-C to exit...
Http Functions:
hello: http://localhost:7071/api/hello
If you see errors, you might be missing a dependency. Get the attention of one of the instructors if you don't know what's wrong from the errors. One of the commons errors that occurs is if you're running an older version of Node.js, it won't install the Node worker properlly and you'll need to update to a new version of Node and reinstall.
Note at the bottom of that output, we have a URL where our Function is hosted: http://localhost:7071/api/hello
Go ahead an make a GET request to that via cURL, Postman, or even just a web browser. You should get a message like Please pass a name on the query string or in the request body
. Now try again with a query parameter of ?name=world
. (aka http://localhost:7071/api/hello?name=world
) which should get you a response like Hello world
. You can try different name values like ?name=trogdor
and see the response change.
While we won't go into detail on all the settings you can do with function.json here, it is worth looking at it and noting we have an httpTrigger
input and an http
output for the response. This is how the runtime knows that this Function is an http triggered Function and not a queue triggered Function.
In our workshop, we're building a service called "squirebot". The idea behind the name is that it is a bot which learns how to do things for you, but you have to train it, much like a squire of old. It is only appropriate then, that our first task we'll want squirebot to do for us is fetch us a lance.
Let's change our function a bit to instead return some ASCII art. You can read through the code, but essentially we have two different templates for long and short lances and we just do a simple find and replace depending on which letters we want it to be made of.
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
if (req.body && req.body.lance_length && req.body.lance_material) {
const long_lance =
` TTT
TTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTT
TTT`
const short_lance =
` TTT
TTTTTTTTTTTT
TTT`
let material = req.body.lance_material === "wood" ? "w" : "m";
let lance = req.body.lance_length === "short" ? short_lance.replace(/T/gi, material) : long_lance.replace(/T/gi, material);
context.res = {
// status: 200, /* Defaults to 200 */
body: {
//card:"hero",
message: `Here's your lance!
${lance}`
}
};
}
else {
context.res = {
status: 400,
body: {
message: "I couldn't figure out how to do that..."
}
};
}
context.done();
};
Now try to run this via cURL or Postman (not your browser since this needs to be POST).
curl -H "Content-Type: application/json" -X POST -d "{\"lance_length\":\"long\",\"lance_material\":\"metal\"}" http://localhost:7071/api/hello
This should return us a fancy ASCII lance. You can now stop your Functions host.
Because this is no longer a hello world Function, and instead a lance fetching Function, one last step is to rename our Function.
The name of your Function is tied to the directory name, which in this case is hello
. You can rename your directory to rename your Function. You can rename your directory from your file explorer, VS Code, or terminal. Rename your directory to "lanceFetcher".
mv ./hello ./lanceFetcher
Now, if you start the funcitons host again, you'll see your API has changed to api/lanceFetcher
. You don't have to change your Function name to change your route - you can also do it by setting the route
property in the function.json
. For example, if I change the route
property to foobar
, can access my function on api/foobar
. If you want to remove api
from the base route, you can do this in the host.json. You can learn about host.json settings on docs.microsoft.com.
Congratulations, you've now completed module 2 and created your first Function. You now know the basics on how to create a Functions project, how to create a Function from a template, how to edit and rename a Function, and how to run the Function locally.
You can create a C# function app in Visual Studio with the same HTTP function as above. Follow the tutorial Create your first function using Visual Studio.