Hey YGREQ,
looking at the code snippet there are a few things to adjust to get the desired outcome. The basic concept with the variables etc is completely on point, however the concept of asynchronicity might be a bit confusing. But let's get to that one by the other...
/ Adding handlers
You are correctly using the maxApi.addHandler functionality here. But keep in mind that the closure (function) is only getting called once the message is received from Max. Basically the apiKey is only set once you send the key message from your Max Patch and therefore not available to the API creation before that.
/ Promise based API calls
Not necessarily important for the problem at hand, but most of the API Functionality is based on Promises. The idea behind this is to help you with scheduling your communication/messaging between the Max and Node Process and ensuring that things have been received. How would one use that easily? Look at the following twist to a portion of your snippet, which uses async/await
Max.addHandler("key", async (keyFromMax) => {
apiKey = keyFromMax;
await Max.outlet(apiKey);
});
You can read more about async/await online and I'm sure your preferred JS tutorial website has a bunch of info on it. These two seemed like a good introduction on a first glance:
/ Instantiating the Binance API
The main culprit of your code seems to be the fact that you create the instance of the Binance API too early, as in before the API credentials have been received from Max. Keeping your current infrastructure you'd have to wrap that into a function itself and only call it once ready. There are multiple ways of course. You could keep your two dedicated messages, turn them into one for further simplifying it or or or.
Let's take the latter approach in the following:
const Binance = require("node-binance-api");
let binanceAPI;
Max.addHandler("set_credentials", async (key, secret) => {
binanceAPI = new Binance().options({
APIKEY: key,
APISecret: secret
});
await Max.outlet(`Set API credentials to key:${key}, secret:${secret}`);
// do the rest from here on
});
This should work. However, if you'd rather avoid wapping the code with all this messaging I'd propose two different ways to solve that, which could bring you back to a bit more synchronous code in the start up phase.
/ Using DotEnv
It's common to use an
.env file for setting environment/configuration variables for your project. One would basically create a
.env_template file to document what needs to be set and add the
.env to your
.gitignore. In order to make that file easily usable you can use the
dotenv package, which does most of the work for you. Imagine a setup like this:
// .env file
BINANCE_API_KEY=my_key
BINANCE_API_SECRET=my_secret
// script.js
require("dotenv").config();
if (!process.env.BINANCE_API_KEY) throw new Error("Missing Binance API Key");
if (!process.env.BINANCE_API_SECRET) throw new Error("Missing Binance API Secret");
const binance = require("node-binance-api")().options({
APIKEY: process.env.BINANCE_API_KEY,
APISECRET: process.env.BINANCE_API_SECRET
});
// do the rest from here
/ Using CLI Arguments
You were saying that part of the motivation was to have the API Credentials be part of your Max Patch. One other, classic option would be to use CLI arguments or even options to pass them to your script at startup time. You could easily append them to the script start message in the patch. A simple way would be to rely simply on order and do something like
script start <api_key> <api_secret>
In your JS script you could then use this like the following using process.argv const [, , apiKey, apiSecret] = process.argv;
if (!apiKey) throw new Error("Missing Binance API Key");
if (!apiSecret) throw new Error("Missing Binance API Secret");
const binance = require("node-binance-api")().options({
APIKEY: apiKey,
APISECRET: apiSecret
});
This is a basic, manual approach to CLI arguments. If you prefer fancier options one could also use packages like
minimist ,
commander or various other ones to achieve the same but it might be a bit overkill for 2 simple arguments.
Anyhow, hope this explains the async behaviour of messaging a bit and furthermore shows a few approaches on how to streamline the configuration of your script!?
Enjoy! :)
Florian