sammie
sammie

Reputation: 91

Azure function run code on startup for Node

I am developing Chatbot using Azure functions. I want to load the some of the conversations for Chatbot from a file. I am looking for a way to load these conversation data before the function app starts with some function callback. Is there a way load the conversation data only once when the function app is started?

This question is actually a duplicate of Azure Function run code on startup. But this question is asked for C# and I wanted a way to do the same thing in NodeJS

Upvotes: 6

Views: 3553

Answers (3)

search-learn
search-learn

Reputation: 1314

After like a week of messing around I got a working solution.

First some context: The question at hand, running custom code @ App Start for Node JS Azure Functions. The issue is currently being discussed here and has been open for almost 5 years, and doesn't seem to be going anywhere.

As of now there is an Azure Functions "warmup" trigger feature, found here AZ Funcs Warm Up Trigger. However this trigger only runs on-scale. So the first, initial instance of your App won't run the "warmup" code.

Solution:

I created a start.js file and put the following code in there

const ErrorHandler = require('./Classes/ErrorHandler');
const Validator = require('./Classes/Validator');
const delay = require('delay');

let flag = false;

module.exports = async () =>
{
    console.log('Initializing Globals')

    global.ErrorHandler = ErrorHandler;
    global.Validator = Validator;

    //this is just to test if it will work with async funcs
    const wait = await delay(5000)

    //add additional logic...
    //await db.connect(); etc // initialize a db connection

    console.log('Done Waiting')
}

To run this code I just have to do

require('../start')();

in any of my functions. Just one function is fine. Since all of the function dependencies are loaded when you deploy your code, as long as this line is in one of the functions, start.js will run and initialize all of your global/singleton variables or whatever else you want it to do on func start. I made a literal function called "startWarmUp" and it is just a timer triggered function that runs once a day.

My use case is that almost every function relies on ErrorHandler and Validator class. And though generally making something a global variable is bad practice, in this case I didn't see any harm in making these 2 classes global so they're available in all of the functions.

Side Note: when developing locally you will have to include that function in your func start --functions <function requiring start.js> <other funcs> in order to have that startup code actually run.

Additionally there is a feature request for this functionality that can voted on open here: Azure Feedback

Upvotes: 3

Ed Y
Ed Y

Reputation: 143

I have a similar use case that I am also stuck on.

Based on this resource I have found a good way to approach the structure of my code. It is simple enough: you just need to run your initialization code before you declare your module.exports.

https://github.com/rcarmo/azure-functions-bot/blob/master/bot/index.js

I also read this thread, but it does not look like there is a recommended solution.

https://github.com/Azure/azure-functions-host/issues/586

However, in my case I have an additional complication in that I need to use promises as I am waiting on external services to come back. These promises run within bot.initialise(). Initialise() only seems to run when the first call to the bot occurs. Which would be fine, but as it is running a promise, my code doesn't block - which means that when it calls 'listener(req, context.res)' it doesn't yet exist.

The next thing I will try is to restructure my code so that bot.initialise returns a promise, but the code would be much simpler if there was a initialisation webhook that guaranteed that the code within it was executed at startup before everything else.

Has anyone found a good workaround?

My code looks something like this:

var listener = null;

if (process.env.FUNCTIONS_EXTENSION_VERSION) {
    // If we are inside Azure Functions, export the standard handler.
    listener = bot.initialise(true);

    module.exports = function (context, req) {
        context.log("Passing body", req.body);
        listener(req, context.res);
    }
} else {
    // Local server for testing
    listener = bot.initialise(false);
}

Upvotes: 0

Alexey Rodionov
Alexey Rodionov

Reputation: 1446

You can use global variable to load data before function execution.

var data = [1, 2, 3];

module.exports = function (context, req) {
    context.log(data[0]);
    context.done();
};

data variable initialized only once and will be used within function calls.

Upvotes: 0

Related Questions