I am writing a Koa middleware that, on each request to the server, analyzes some input from a file on the server, and sends it over the websocket (using https://www.npmjs.com/package/ws) to the client.
This all works as expected, except for one problem. The data (as a result of analyzing the input from the file) updates properly, but doesn't update inside the websocket callback scope.
const websocket = new WebSocket.Server({ port: 8082 });
const sourceAST = parseJS();
const conditionResults = getConditionResults(sourceAST);
console.log('handling request', conditionResults); // <-- updates as expected every time
websocket.on('connection', ws => {
console.log(conditionResults); // <-- not updated :(
ws.send(
JSON.stringify({
type: 'feedback-updated',
feedback: conditionResults,
}),
);
});
I can't seem to figure out why the conditionResults inside the ws callbacks is frozen on the first time it is ran, why it doesn't update each time this code is ran (on every request).
Edit: for context, the code snippet above lives inside a middleware function like this:
myMiddleware = async (ctx, next) => {
await next();
// my code snippet above
}
And the middleware is ran in Koa like so:
app.use(myMiddleware);
If your middleware function can be initiated by another process, as well as a websocket message, something like this should work:
const websocket = new WebSocket.Server({port: 8082});
let sourceAST;
let conditionResults;
// Triggered by websocket message
websocket.on('message', data => {
doThings()
});
// Triggered by your middleware
myMiddleware = async (ctx, next) => {
await next();
doThings()
};
function doThings () {
sourceAST = getConditionResults(sourceAST);
conditionResults = getConditionResults(sourceAST);
console.log(conditionResults);
websocket.send(
JSON.stringify({
type: 'feedback-updated',
feedback: conditionResults,
}),
);
}
Related
Can I call an imported function in app.get("/")? is it good practice?
Does this also mean that all logic must be done in the addArticle function?
post.js
const addArticle = async post => {
const postData = {
body: post.body,
title: post.title,
username: post.username
};
const { data } = await axios.post(
`${BASE_URL}/data/`,
postData
);
return data;
}
catch (err) {
console.log(err)
}
index.js
const express = require('express')
const axios = require('axios')
const postfunc = require (./post.js)
const app = express()
app.get("/", post.addArticle)
app.listen(3001)
It’s acceptable to pass your route handler however you see fit. However, your addArticle function doesn’t conform to express route handler signature.
Your function should handle two arguments, being express.Request and express.Response instances respectively.
Finally, note that returning a value from a route handler does nothing — if you want to send that value to the client, you’ll need to use res.send or similar method.
I am using a Next.js API call to retrieve some data from an external API (I know this is better done with getStaticProps but I need to make a few calls before passing the data to the client.)
I don't understand why doe the backend function runs multiple times, and on the first time it doesn't have the POST data.
cleint
export default function Customer () {
const router = useRouter()
const { id: submissionId } = router.query
const { data: customer, error } = useSWR(['/api/jotform/get-client/', submissionId], fetcher)
...
server
export default async function handler (req, res) {
try {
const customers = await axios.get('https://api.jotform.com/form/' + jotformAPI.form_id + '/submissions?apiKey=' + jotformAPI.key)
const { submissionId } = req.body
const customer = customers.data.content.find(client => client.id === submissionId)
if (!customer) throw new Error('contact does not exist')
//on the first call the error is triggered, only on the second time I get the data.
...
I am trying to export database properties stored in properties file from Javascript module. By the time I read database properties file, Javascript file is already exported and data properties appear undefined wherever I use in other modules.
const Pool = require('pg').Pool;
const fs = require('fs')
const path = require('path');
class DbConfig {
constructor(dbData) {
this.pool = new Pool({
user: dbData['user'],
host: dbData['host'],
database: dbData['database'],
password: dbData['password'],
max: 20,
port: 5432
});
}
}
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
if (err) {
console.error(err)
return
}
// dbData = {"user":"postgres", "password": "1234"...};
return dbData;
});
}
let db = new DbConfig(getdbconf());
let dbPool = db.pool;
console.log("dbpool : -> : ",dbPool); // username and password appear undefined
module.exports = { dbPool };
Is there a way to read data before exporting data from Javascript module?
Usually database config or any other sensitive info is read from a .env file using dotenv .
Or
you could also provide env from command line itself like
DB_HOST=127.0.0.1 node index.js
inside your index.js
console.log(process.env.DB_HOST)
Please create a new file (connection-pool.js) and paste this code:
const { Pool } = require('pg');
const poolConnection = new Pool({
user: 'postgresUserName',
host: 'yourHost',
database: 'someNameDataBase',
password: 'postgresUserPassword',
port: 5432,
});
console.log('connectionOptions', poolConnection.options);
module.exports = poolConnection;
For use it, create a new file (demo-connection.js) and paste this code:
const pool = require('./connection-pool');
pool.query('SELECT NOW();', (err, res) => {
if (err) {
// throw err;
console.log('connection error');
return;
}
if (res) {
console.log(res.rows);
pool.end();
}
});
This is an alternative option 🙂
Exporting the result of async calls
To export values which have been obtained asynchronously, export a Promise.
const fs = require('fs/promises'); // `/promise` means no callbacks, Promise returned
const dbDataPromise = fs.readFile('fileToRead')); //`readFile` returns Promise now
module.exports = dbDataPromise;
Importing
When you need to use the value,
const dbDataPromise = require('./dbdata');
async init() {
const dbData = await dbDataPromise;
}
//or without async, using Promise callbacks
init() {
dbDataPromise
.then(dbData => the rest of your code that depends on dbData here);
}
Current code broken
Please note that your current code, as pasted above, is broken:
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
//[...] snipped for brevity
return dbData;
});
}
fs.readFile "returns" dbData, but there is nothing to return to, since you are in a callback which you did not call yourself. Function getdbconf returns nothing.
The line that says let db = new DbConfig(getdbconf()); will NOT work. It needs to be inside the callback.
The only way to avoid putting all of your code inside the callback (and "flatten" it) is to use await, or to use readFileSync
Avoiding the issue
Using environment variables
Suhas Nama's suggestion is a good one, and is common practice. Try putting the values you need in environment variables.
Using synchronous readFile
While using synchronous calls does block the event loop, it's ok to do during initialization, before your app is up and running.
This avoids the problem of having everything in a callback or having to export Promises, and is often the best solution.
Firstly, I'm a frontend developer so I'm sorry if I use wrong terms in my explanations.
In my company, we are actually building 1 web app and 2 API apps. So the users use the web app which talks to the first API which talks to the second API.
Here, we are in the first API, in the server.js file:
server.js
---------
var app = express();
const cats = require("./api/cats");
app.get("/animals/cats", cats.listTheCats); // listTheCats() returns an array of cats
In cats.js, we can see with listTheCats() we are sending another request to the second API:
cats.js
-------
const listTheCats = (req, res) => {
axios({
method: "get",
url: "http://second-api-url.com/animals/cats",
params: req.query,
})
.then((ans) => {
res.status(ans.data.status).json(ans.data.data);
})
.catch((err) => {
console.log(err);
res.sendStatus(503);
});
};
module.exports = listTheCats;
The code above works fine on the web app. But now, in the first api, in another file called "cuteAnimals.js" I need to call listTheCats(). So I tried to do this but it doesn't work:
cuteAnimals.js
--------------
const { listTheCats } = require("./cats");
const fetchCats = async () => {
const params = {
type: "cute",
};
const cuteCats = await axios.get(`animals/cats`, {
params,
});
// or const cuteCats = await listTheCats(params);
console.log("cuteCats", cuteCats);
};
fetchCats();
This is the error: "Request failed with status code 400"
In cuteAnimals.js, is it right to use axios from a file to another file of the same server project?
You need to export the function in order to use it in another file, you can do it simply by writing this line at the end of cats.js
module.exports = listTheCats
im pretty new into javascript and node, currently working into a node.js app,
the app use express and mongoDB, the idea is listen to some third party services via webhook, websocket and mqtt and store all data into mongoDB.
but I have a litle problem, some of the third party apps send me data too often,
for example, the mqtt stream sends about 2 message every second, i need to store only one of those message every minute.
this is the way I instance mqtt into app.js
var mqttHandler = require('./mqtt/mqtt_handler'); //mqtt
var mqttClient = new mqttHandler(); //mqtt
mqttClient.connect(); //mqtt
this is my mqttHandler.js:
onst mqtt = require('mqtt');
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = 'mqtts://host';
this.username = 'foo'; // mqtt credentials if these are needed to connect
this.password = 'mypassqword';
this.port = 8083;
this.protocol = 'MQTTS';
this.client = 'bar'
}
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, {password : this.password, username : this.username, port: this.port});
// Mqtt error calback
this.mqttClient.on('error', (err) => {
console.log(err);
this.mqttClient.end();
});
// Connection callback
this.mqttClient.on('connect', () => {
//console.log(`mqtt client connected`);
});
// mqtt subscriptions
this.mqttClient.subscribe('/the_subscription');
// When a message arrives, console.log it
this.mqttClient.on('message', function (topic, message) {
console.log(message.toString())
});
this.mqttClient.on('close', () => {
//console.log(`mqtt client disconnected`);
});
}
// Sends a mqtt message to topic: mytopic
sendMessage(message) {
this.mqttClient.publish('mytopic', message);
}
}
module.exports = MqttHandler;
i'veing reading about setInterval and setTimeout, but I can't figure out how to implement these to force a given function to only run once every X seconds (no mather how many times it is called)
could there be a similar / generic way to implement this feature for both, mqtt, webohooks and / or websocket?
I took this example about how to implement mqtt from a tutorial, its working perfect, as I said, im prettty new to javascript.
One naive approach using setInterval is to set a flag regularly and clear it once a message is posted. The ignore any other messages until the flag is set again by the interval function.
let readyToPost = false;
setInterval(function(){ readyToPost = true; }, 1000);
In your function:
function connect() {
if (!readyToPost) return; // do nothing
readyToPost = false;
// rest of your code
}
There is also a wrapper of the module mqtt:
const mqttNow = require('mqtt-now');
const options = {
host: 'localhost',
interval: 1000,
actions: [
{
topic: 'public',
message: 'my message'
},
{
topic: 'random',
message: () => ( 'random ' + Math.random() )
}
]
}
mqttNow.publish(options);