I am trying to set up a simple serverless function on Netlify just to test out usage of environment variables. I have defined the following two environment variables in Netlify for my site:
Variable Name
Value
ALPHABET_SEPARATION
2
CHARS_BETWEEN
3
I have also updated my functions directory as follows:
Functions directory: myfunctions
I am using continuous deployment from github. As I do not know the use of npm at present and finding it convenient to directly test the production deploy, I have defined a subdirectory called myfunctions inside my root directory and have placed my javascript file containing the "serverless" function inside it on my local machine. I have built in logic so that the "serverless" function gets called only when a "netlify" flag is set, otherwise, an alternate function gets executed client-side. Basically it works as follows:
const deploy = "netlify" //Possible valid values are "local" and "netlify"
async function postRandomString() {
const stringToUpdate = "THISISATESTSTRING"
var stringToPost = "DUMMYINITIALVALUE";
if (deploy === "local") {
stringToPost = updateString(stringToUpdate); //updateString is a function defined elsewhere and executes client-side;
}
else if (deploy === "netlify") {
const config = {
method: 'GET',
headers: {
'Accept': 'application/json',
}
};
const res = await fetch(`myfunctions/serverUpdateString?input=${stringToUpdate}`, config);
const data = await res.json();
stringToPost = data.retVal;
console.log(data.retVal);
}
else {
stringToPost = "##ERROR##";
}
postString(stringToPost); //postString is a function defined elsewhere and executes client-side;
}
The serverless function file serverUpdateString.js is coded as follows (it basically sets a character at a certain position (determined by CHARS_BETWEEN) in the string to an alphabetical character which is a certain number (determined by ALPHABET_SEPARATION) of places in the alphabet after the first character of the string (don't ask why - the point is that it never even receives/handles the request):
exports.handler = async function (event) {
const { CHARS_BETWEEN, ALPHABET_SEPARATION } = process.env;
const charsBetween = CHARS_BETWEEN;
const alphabetSeparation = ALPHABET_SEPARATION;
const initString = event.queryStringParameters.input;
const rootUnicode = initString.charCodeAt(0);
const finalUnicode = "A".charCodeAt(0) + (rootUnicode - "A".charCodeAt(0) + alphabetSeparation) % 26;
const finalChar = String.fromCharCode(finalUnicode);
const stringArray = initString.split("");
stringArray[charsBetween + 1] = finalChar;
const stringToReturn = stringArray.join("");
const response = {
statusCode: 200,
retVal: stringToReturn,
}
return JSON.stringify(response);
}
When I run it, I get a 404 error for the GET request:
In the above image, script.js:43 is the line const res = await fetch(myfunctions/serverUpdateString?input=ATESTSTRIN, config); in the calling file, as shown in the first code block above.
What am I doing incorrectly? Surely Netlify should be able to pick up the serverless function file given that I have specified the folder alright and have placed it at the right place in the directory structure? I have given the whole code for completeness but the problem seems quite elementary. Look forward to your help, thanks.
I got assistance from Netlify forums. Basically the following changes needed to be made:
The fetch request -- line 43 in the calling code (script.js) -- needed to be changed to
const res = await fetch(`https://netlifytestserverless.netlify.app/.netlify/functions/serverUpdateString?input=${stringToUpdate}`, config);
The return statement in the lambda function needed to be changed to:
const response = {
statusCode: 200,
body: JSON.stringify(stringToReturn),
}
Other minor changes such as using parseInt with the environment variables.
The code works now.
Related
I wanted to write a understandable code in node.js, so I want to put some functions, which are used very often, into other node.js files, and access them from there.
So I get a function, which calls a function from another node.js file and in this other node.js file, also another one is called.
Important to know, if I put all in one file, the code works, so it should be an issue with module export and using functions in another file.
I have one file, getting quotes from a decentralised exchange. Looking like this (quoter_uni_v2.js):
module.exports = function quotes_uni_v2(tokenIn, tokenOut, amountIn, router) {
const quotedAmountOut = router.getAmountsOut(amountIn.toString(), [
tokenIn,
tokenOut,
]);
return quotedAmountOut;
};
And I am importing this function in my second helper file (quotes_5.js) (It is splitted in two files, because in the second one I have to call the function multiple times):
var quotes_uni_v2 = require("./quotes_uni_v2");
module.exports = async function (router1, router2, route, amount_wei) {
console.log(route);
var amount_Out = await quotes_uni_v2.quotes_uni_v2(
route[1],
route[2],
amount_wei,
router1
);
...
return (
Math.round(ethers.utils.formatEther(amount_Out[1].toString()) * 100) / 100
);
};
After that I try to call the function in my main.js:
const quotes_uni_v2 = require("./quotes_uni_v2");
const quotes_5 = require("./quotes_5");
async function calc(route) {
amountOut = await new quotes_5(
quickswap_router,
sushiswap_router,
route,
amount_wei
);
return amountOut;
};
But calling the quotes function does not work... The error is:
TypeError: quotes_5 is not a constructor...
Can someone help me?
Thanks!
I've never created a Javascript module/library before so this is a bit new to me so apologizes for my lack of knowing what to google.
I'm creating a library that will hold information from a URL that is provided by a user. I want to parse the URL's path (the part that comes after the domain) as well as retain a header value that's provided by the URL's response.
It's basic but here's what I have so far:
function Link(someURL) {
this.url = someURL;
this.urlPath = "";
this.uuid = "";
this.getPath = function (someURL) {
// do regexp parsing and return everything after the domain
};
this.getUUID = function (someURL) {
// fetch the URL and return what is in the response's "uuid" header
}
}
Ideally, I'd the module to automatically get all the information upon construction:
var foo = new Link("http://httpbin.org/response-headers?uuid=36d09ff2-4b27-411a-9155-e82210a100c3")
console.log(foo.urlPath); // should return "uuid"
console.log(foo.uuid); // should return the contents in the "uuid" header in the response
How do I ensure the this.urlPath and this.uuid properties get initialized along with this.url? Ideally, I'd only fetch the URL once (to prevent rate limiting by the target server).
After a lot of trial and error, I ended up doing something more like this:
class Link {
constructor (url_in) {
const re = RegExp("^https://somedomain.com\/(.*)$");
this.url = re[0];
this.linkPath = re[1];
}
async getUUID() {
const res = await fetch("https://fakedomain.com/getUUID?secret=" + this.linkPath);
this.uuid = res.uuid;
}
async getJSON() {
const res = await fetch("https://fakedomain.com/getJSON?uuid=" + this.uuid);
this.json = await res.json();
}
async initialize() {
await this.getUUID();
await this.getJSON();
}
}
const someLinkData = new Link("https://reallydumbdomain.com/2020/10/4/blog");
someLinkData.initialize()
.then(function() {
console.log(this.json); // this now works
});
I think a future iteration of this will require me to send a promise with the initialize function but for now, this works.
Hi I'm currently trying to figure it out how to properly define global variable in node.js. I know it is not a good practice to do this, but in this particular screnario it's the only way to do this without using connection to database.
I'm getting data from github API to display some information, I'm trying to store them in the global variable. It should allow me to e.g pass specific object from first global list to new global list that display only chosen items.
I have file called utils.js that have this two empty arrays that should be global:
let repoItems = [];
let bookmarkedItems = [];
exports.repoItems = repoItems;
exports.bookmarkedItems = bookmarkedItems;
Then I have another file that fetch and should assign items to the first global variable, but it looks like it doesn't. Because in the moment I'm trying to chose one item & push it into second empty array it's impossible, I'm getting empty array. I'm not sure if the mistake is taken because of bad implementation of global variable from other file, or something else :/
Below I include the fetching part of code, with bold part that I'm confused with:
let utils = require('../utils');
let {
repoItems,
} = utils;
router.get('/', async (req, res) => {
try {
const result = await fetchGithubAPI(`searching word`);
const urls = result.map(url => url);
console.log(urls);
res.render('repositories.ejs', {
'data': urls
});
} catch (e) {
console.log(e);
}
});
async function fetchGithubAPI(search) {
const response = await fetch(`https://api.github.com/?q=${search}`, {
method: 'GET',
headers: {
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json',
},
});
const data = await response.json();
**repoItems = data.items.map(item => item);**
return repoItems;
}
Try:
let repoItems = require('./utils').repoItems
instead of
let {
repoItems,
} = utils;
if you remove
let {
repoItems,
} = utils;
you can try using
utils.repoItems = data.items.map(item => item)
I tried an example setup for it
--utils.js
module.exports.someValue = 3;
module.exports.setSomeValue = (value) => {someValue = value}
--index.js
const utils = require('./utils');
console.log(utils.someValue);// 3
utils.someValue = 5
console.log(utils.someValue);// 5
Update after knowing about getter and setter methods in Js
You can refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
this is another way to change value of any private properties in JS
I am invoking a functionA from another functionB in the HTML file. Basically, this file contains a couple of JS functions.
The following is functionA (getBranchAndChangeIdForSideBySideDiffView)
function getBranchAndChangeIdForSideBySideDiffView() {
const match = parseGerritURLForSideBySideDiffView();
const project = match[1];
const change = match[2];
const url = '/changes/' + '?q=' + encodeURIComponent(project) +
'&o=CURRENT_REVISION&o=ALL_REVISIONS';
let branch = '';
let changeId = '';
coveragePlugin.restApi().get(url).then((response) => {
branch = response[0].branch;
changeId = response[0].change_id;
return [branch, changeId];
console.log('branch is', branch);
console.log('changeId is', changeId);
}).catch((err) => {
console.log('Failed to get change details for code coverage:', err);
});
}
The GET API is described here as follows (https://gerrit.googlesource.com/gerrit/+/refs/heads/stable-2.16/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js#90).
And the functionB is simple which just calls functionB (also has other lines of code, the following lines of code are where I am calling functionA).
let response = getBranchAndChangeIdForSideBySideDiffView();
coverageData.changeId = response[0];
coverageData.branch = response[1];
coverageData.project = match[0];
coverageData.change = match[1];
coverageData.patchset = match[2];
Now, what's strange is, when I simply enter the full URL in my browser, a JSON file is downloaded. But, when this code executes in the browser, after the GET call is made, the line of execution simply moves to the end of functionB. It's like, it doesn't go inside the curly brackets. I am not sure where I am going wrong? Can anyone help me with it?
I have the following code that does not work currently.
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models.
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: true, force: false }).then(function() {
models.users.create({
name: 'joe',
loggedIn: true
}).then( task => {
console.log("saved user!!!!!");
});
process.exit();//close the nodeJS Script
}).catch(function(error) {
console.log(error);
});
sequelize-lib.js
var Sequelize = require('sequelize');
exports.getSequelizeConnection = function(stage){
var argv = require('minimist')(process.argv.slice(2)); //If this file is being used in a script, this will attempt to get information from the argument stage passed if it exists
//Change connection settings based on stage variable. Assume localhost by default.
var dbname = argv['stage'] ? argv['stage']+"_db" : 'localdb';
var dbuser = argv['stage'] ? process.env.RDS_USERNAME : 'admin';
var dbpass = argv['stage'] ? process.env.RDS_PASSWORD : 'local123';
var dbhost = argv['stage'] ? "database-"+argv['stage']+".whatever.com" : 'localhost';
//If state variable used during require overide any arguments passed.
if(stage){
dbname = stage+"_db";
dbuser = process.env.RDS_USERNAME
dbpass = process.env.RDS_PASSWORD
dbhost = "database-"+stage+".whatever.com"
}
var connection = new Sequelize(dbname,dbuser,dbpass, {
dialect: 'mysql',
operatorsAliases: false, //This gets rid of a sequelize deprecated warning , refer https://github.com/sequelize/sequelize/issues/8417
host: dbhost
});
return connection;
}
exports.setModels = function(connection){
//Import all the known models for the project.
const fs = require('fs');
const dir = __dirname+'/../models';
var models = {}; //empty model object for adding model instances in file loop below.
//#JA - Wait until this function finishes ~ hence readdirSync vs regular readdir which is async
fs.readdirSync(dir).forEach(file => {
console.log(file);
//Split the .js part of the filename
var arr = file.split(".");
var name = arr[0].toLowerCase();
//Create a modle object using the filename as the reference without the .js pointing to a created sequelize instance of the file.
models[name] = connection.import(__dirname + "/../models/"+file);
})
//Showcase the final model.
console.log(models);
return models; //This returns a model with reference to the sequelize models
}
I can't get the create command to work however with this setup. My guess is the variables must not be passing through correctly somehow. I'm not sure what I'm doing wrong?
The create command definitely works because if in the sequelize-lib.js I modify the setModels function to this...
exports.setModels = function(connection){
//Import all the known models for the project.
const fs = require('fs');
const dir = __dirname+'/../models';
var models = {}; //empty model object for adding model instances in file loop below.
//#JA - Wait until this function finishes ~ hence readdirSync vs regular readdir which is async
fs.readdirSync(dir).forEach(file => {
console.log(file);
//Split the .js part of the filename
var arr = file.split(".");
var name = arr[0].toLowerCase();
//Create a modle object using the filename as the reference without the .js pointing to a created sequelize instance of the file.
models[name] = connection.import(__dirname + "/../models/"+file);
models[name].create({
"name":"joe",
"loggedIn":true
});
})
//Showcase the final model.
console.log(models);
return models; //This returns a model with reference to the sequelize models
}
Then it works and I see the item added to the database! (refer to proof image below)
Take note, I am simply running create on the variable at this point. What am I doing wrong where the model object is not passing between files correctly? Weird part is I don't get any errors thrown in the main file?? It's as if everything is defined but empty or something and the command is never run and nothing added to the database.
I tried this in the main file also and no luck.
models["users"].create({
name: 'joe',
loggedIn: true
}).then( task => {
console.log("saved user!!!!!");
});
The purpose of this all is to read models automatically from the model directory and create instances that are ready to go for every model, even if new one's are added in the future.
UPDATE::
So I did another test that was interesting, it seems that the create function won't work in the .then() function of the sync command. It looks like it was passing it correctly though. After changing the front page to this...
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models using connection previosly created.
models["users"].create({
"name":"joe",
"loggedIn":true
});
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: true, force: false }).then(function() {
process.exit();//close the nodeJS Script
}).catch(function(error) {
console.log(error);
});
Doing this seems to get create to work. I'm not sure if this is good form or not though since the database might not be created at this point? I need a way to get it to work in the sync function.
Well I answered my question finally, but I'm not sure I like the answer.
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models using connection previosly created.
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: false, force: false }).then( () => {
models["users"].create({
"name":"joe",
"loggedIn":true
}).then( user => {
console.log("finished, with user.name="+user.name);
process.exit();
}).catch( error => {
console.log("Error Occured");
console.log(error);
});
}).catch(function(error) {
console.log(error);
});
turns out that process.exit was triggering before create would occur because create happens async. This means that all my code will have to constantly be running through callbacks...which seems like a nightmare a bit. I wonder if there is a better way?