With the recent updates to the [serverless-azure-functions](https://github.com/serverless/serverless-azure-functions/blob/master/CHANGELOG.md)
plugin, it is now easier than ever to create, deploy and maintain a real-world REST API running on Azure Functions. This post will walk you through the first few steps of doing that.
To see the full end-to-end example used to create this demo, check out my GitHub repo. I structured each commit to follow the steps described in this post. Any steps named Step X.X
are steps that involve no code or configuration changes (and thus not tracked by source control), but actions that could/should be taken at that point in the process. This is done to preserve the “commit-per-step” structure of the example repo.
This post will only cover the basics of creating and deploying a REST API with Azure Functions, which includes step 1 and step 2 from the example repo. Stay tuned for posts on the additional steps in the future.
I will make the assumption that you have the Serverless Framework installed globally. If you do not (or have not updated in a while), run:
npm i serverless -g
Also, the serverless
CLI can be referenced by either serverless
or sls
. I will use sls
in this post just because it’s shorter, but serverless
would work just the same.
Let’s begin by creating our Azure Function project with a template from serverless.
sls create -t azure-nodejs -p sls-az-func-rest-api
The resulting project will be in the directory sls-az-func-rest-api
. cd
into that directory and run npm install
. To make sure you have the latest version of the Azure Functions plugin, run:
npm install serverless-azure-functions --save
It’s important to note that the generated serverless.yml
file will contain a lot of commented lines, which start with #
. Those are purely for your benefit in exploring features of the Azure Functions plugin, and can be safely removed.
For the sake of this demo, we’re going to create a basic wrapper of the GitHub API for issues and pull requests.
As you’ve probably already noticed, the azure-nodejs
template comes preloaded with two functions: hello
and goodbye
. Let’s remove those before we start adding our own code. To do this, remove both the hello.js
and goodbye.js
files. Also, remove their configuration definitions from serverless.yml
.
Right now your file structure should look something like:
sls-az-func-rest-api
|-- host.json
|-- package.json
|-- README.md
|-- serverless.yml
and your serverless.yml
should look like (not including any comments):
1
service: sls-az-func-rest-api
2
3
provider:
4
name: azure
5
location: East US
6
runtime: nodejs10.x
7
8
plugins:
9
- serverless-azure-functions
10
11
package:
12
exclude:
13
- local.settings.json
14
- .vscode/**
15
16
functions:
Let’s add in our own code. We’ll start by creating the directory src/handlers
. This, perhaps to your great surprise, will be where our handlers will live. Inside that directory, we will put our two handlers: issues.js and pulls.js.
1
// src/handlers/issues.js
2
3
const utils = require("../utils");
4
const axios = require("axios");
5
6
module.exports.handler = async (context, req) => {
7
context.log("Issue Handler hit");
8
9
const owner = utils.getQueryOrBodyParam(req, "owner");
10
const repo = utils.getQueryOrBodyParam(req, "repo");
11
12
if (owner && repo) {
13
const response = await axios({
14
url: `https://api.github.com/repos/${owner}/${repo}/issues`,
15
method: "get"
16
});
17
context.res = {
18
status: 200,
19
body: response.data
20
};
21
} else {
22
context.res = {
23
status: 400,
24
body: "Please pass the name of an owner and a repo in the request"
25
};
26
}
27
};
1
// src/handlers/pulls.js
2
3
const utils = require("../utils");
4
const axios = require("axios");
5
6
module.exports.handler = async (context, req) => {
7
context.log("Pull Request Handler hit");
8
9
const owner = utils.getQueryOrBodyParam(req, "owner");
10
const repo = utils.getQueryOrBodyParam(req, "repo");
11
12
if (owner && repo) {
13
const response = await axios({
14
url: `https://api.github.com/repos/${owner}/${repo}/pulls`,
15
method: "get"
16
});
17
context.res = {
18
status: 200,
19
body: response.data
20
};
21
} else {
22
context.res = {
23
status: 400,
24
body: "Please pass the name of an owner and a repo in the request"
25
};
26
}
27
};
#api #serverless