Deploying a node prototype to the free Azure tier

·

4 min read

Pre-reqs

Azure Subscription of some type Login Node (I used version 14) VS Code & install Azure App Service extension

Helpful links

MS Quick Start

Steps

Sign into Azure

Go to VS code and find the Azure symbol on the left icon bar

Sign in to Azure through VS Code

After doing the sign in thing you should see this

VS Code Azure explorer showing subscriptions

Set up your project

Note: Your setup can vary. This is how the sample project was organized and I just kept it as similar as possible as I think Azure does its magic using some conventions that aren't exactly apparent.

  1. Create a bin folder in the project root.

  2. Create a www file. No extension. Just www.

image.png image.png

The first line of the file needs to contain

#!/usr/bin/env node

This lets everyone know you're serious and this is an actual javascript file, even without the silly js extension.

Write your node server

Sails, Express, Hapi, or just plain node? Doesn't matter. The main constraint is: you can only have one server running on a single port per Azure App Service. For prototyping I've used json-server which seems to fit my current needs.

The big thing to pay attention to here is that Azure is going to set its own port for you through azure magic. If you try to serve something on port 80, 443, 3000, 4200 you are most likely going to be sad. There's an environment variable that Azure will setup through the docker instance that it creates and you need to pick up on it. You can still specify whatever port you want to locally and it'll work, just be forwarned. Your code to pick up the port will need to look something like this:

const port = process.env.PORT || 3000;

Note If this doesn't work for you check the sample code at the end of this article. There is a possibility that Azure could have setup the port as a named pipe instead.

At this point you might notice that even though VSCode agrees with you that it's a JS file, there isn't any error checking. For a sanity check I would run your server manually (ex node ./bin/www) and make sure everything looks good to go.

The last very important configuration step you will want to make sure is correct is to look at your build task inside of your package.json file in the root of your project. This is the task that Azure will run when you deploy in the next step. For an Angular project this will most likely look something like this: ng build --configuration production

Note As of this writing I cannot find where to specify what build task is kicked off. I looked all over the app service's settings and could not find anything. I believe if you need further control over this you need to setup an Azure pipeline which might not be apart of the Free tier.

Note Updates to your app seem to be linked to the git commits on the master branch. The commits don't neccessarily need to be pushed to the git repository.

Deploy it

Head on over to your Azure extension tab. Hover over the App Service pane. You should see a menu up at the top. Click the "Deploy to Web App" button.

image.png

  1. Create New Web App

  2. Give it a name, it needs to be unique to all of Azure

  3. Choose the latest Node version (14 at the time this was written)

  4. Choose the Free tier because free things are great

  5. Confirm that the deployment starts and take a 5 minute break because this takes a moment.

  6. If an option comes up to Always Deploy the workspace... click Yes and it'll be saved to this workspace. Super handy!

  7. Wait forever and click on the Browse Website button when complete. You can also right click on the app service and click Browse Website later if you don't know the URL.

Deploy again

Just come back to the same window and click the same "Deploy to Web App" button. Confirm and watch it go. Good job :D

Sample bin/www file:

#!/usr/bin/env node
// The bash looking command above is required for Azure's magic

const path = require("path");
const jsonServer = require("json-server");
require("dotenv").config(); // Load any env files available

const pathToJsonFile = path.join(__dirname, "../src/app/services/db.json");
const router = jsonServer.router(pathToJsonFile);
const middlewares = jsonServer.defaults();

const expressServer = jsonServer.create(); // By default it serves static files out of the ./public path
var port = normalizePort(process.env.PORT || "3000"); // By default your Azure instance will have it's own env.PORT value

expressServer.use(middlewares);
expressServer.use(router);

expressServer.listen(port, () => {
  console.log(`Server listening on port: ${port}`);
});
expressServer.on("error", onError);

function onError(error) {
  if (error.syscall !== "listen") {
    throw error;
  }

  var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;

  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges");
      process.exit(1);
    case "EADDRINUSE":
      console.error(bind + " is already in use");
      process.exit(1);
    default:
      throw error;
  }
}

function normalizePort(val) {
  var portNumber = parseInt(val, 10);

  if (isNaN(portNumber)) {
    const namedPipe = val;
    return namedPipe;
  }

  if (portNumber >= 0) {
    return portNumber;
  }

  return false;
}