Fire and forget worker inside controller - node.js

I need to create a simple solution to receive input from an user, query our database and return the result in any way, but the queries can take as long as half an hour to run (and our cloud is configured to timeout after 2 minutes, I'm not allowed to change that).
I made the following solution that works locally, and want to include code to send the query's result via email to the user (in a fire and forget manner), but am unsure as how to do that while returning HTTP 200 to the user.
index.js:
const express = require('express')
const bodyParser = require('body-parser')
const db = require('./queries')
const app = express()
const port = 3000
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true,
})
)
app.get('/', (request, response) => {
response.json({ info: 'Node.js, Express, and Postgres API' })
})
app.post('/report', db.getReport)
app.get('/test', db.getTest)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
queries.js:
const Pool = require('pg').Pool
const pool = new Pool({
user: 'xxx',
host: 'xx.xxx.xx.xxx',
database: 'xxxxxxxx',
password: 'xxxxxxxx',
port: xxxx,
})
const getReport = (request, response) => {
const { business_group_id, initial_date, final_date } = request.body
pool.query(` SELECT GIANT QUERY`, [business_group_id, initial_date, final_date], (error, results) => {
if (error) {
throw error
}
response.status(200).json(results.rows)
})
// I want to change that to something like:
// FireNForgetWorker(params)
// response.status(200)
}
module.exports = {
getReport
}

Through the use of callbacks, and based on the design of express, you can send a response and continue to perform actions in that same function. You can, therefore, restructure it to look something like this:
const Pool = require('pg').Pool
const pool = new Pool({
user: 'xxx',
host: 'xx.xxx.xx.xxx',
database: 'xxxxxxxx',
password: 'xxxxxxxx',
port: xxxx,
})
const getReport = (request, response) => {
const { business_group_id, initial_date, final_date } = request.body
pool.query(` SELECT GIANT QUERY`, [business_group_id, initial_date, final_date], (error, results) => {
if (error) {
// TODO: Do something to handle error, or send an email that the query was unsucessfull
throw error
}
// Send the email here.
})
response.status(200).json({message: 'Process Began'});
}
module.exports = {
getReport
}
=============================================================================
Another approach could be to implement a queuing system that would push these requests to a queue, and have another process listening and sending the emails. That would be a bit more complicated though.

Related

Question about Environment/ Config Vars- Heroku and Postgres

Ive managed to slowly grind my way through the curriculum and just completed the backend portion.
I decided to look into building some practice project using Heroku…
If this is not the place to post his please let me know.
While following this tutorial https://devcenter.heroku.com/articles/getting-started-with-nodejs, I get to the portion where we connect to the Postgres server (‘Provision a database’). I do this successfully but I get side tracked on figuring out how to access/use this Postgres database locally when it is located and provided through Heroku. Please consider the following two methods:
Method #1
const cool = require("cool-ascii-faces");
const express = require("express");
const path = require("path");
const PORT = process.env.PORT || 5000;
const { Pool } = require("pg");
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
},
});
express()
.use(express.static(path.join(__dirname, "public")))
.set("views", path.join(__dirname, "views"))
.set("view engine", "ejs")
.get("/", (req, res) => res.render("pages/index"))
.get("/db", async (req, res) => {
try {
const client = await pool.connect();
const result = await client.query("SELECT * FROM test_table");
const results = { results: result ? result.rows : null };
res.render("pages/db", results);
client.release();
} catch (err) {
console.error(err);
res.send("Error " + err);
}
})
.get("/cool", (req, res) => res.send(cool()))
.get("/times", (req, res) => res.send(showTimes()))
.listen(PORT, () => console.log(`Listening on ${PORT}`));
const showTimes = () => {
let result = "";
const times = process.env.TIMES || 5;
for (i = 0; i < times; i++) {
console.log(i);
result += i + " ";
}
return result;
};
result form method #1
Method #2
Exactly the same code from Method #1 except I take the following steps prior to running:
Kill and reopen terminal at file location (/node-js-getting-started)> Using the credentials information (User, Password, Host, Port, and Database) in the heroku datastore resources tab I run the following in order:
% export DATABASE_URL=postgres://User:Password#Host:Port/Database
% heroku local web <--This runs app # localhost:5000
results from method #2
My question is, why does the one connect and the other doesn’t?

Why my restful API stuck when I put integer as parameter in the url using node.js

I'm trying to get data in JSON format. I just copied an old project and changed it IP address to database, username, port, password and database name.
When I try to access data through this addres: localhost:3000/&id=13
The browser just doesn't load them.
When I enter the address with the port without / I see the message with error:
return res.status(500).json({ error: "Грешна заявка. Опитай отново !"})
The same code is pinned to another database and I see the data in JSON format.
I checked 10 times if the username, password, port and database name are correct and they are fine.
The code:
// Create express app
var express = require("express")
var app = express()
var mysql = require('mysql')
var express = require("express")
var cors = require('cors')
app.use(cors())
// Server port
var HTTP_PORT = 3000
var pool = mysql.createPool({
connectionLimit: 10,
host: '192.168.0.1',
user: 'user',
port: '3388',
password: 'password',
database: 'databasename'
});
var ardaforecast = '';
app.route('/')
.get(function (req, res) {
// omitted
res.setHeader('Access-Control-Allow-Origin', '*', 'Cache-Control', 'private, no-cache, no-store, must-revalidate');
//const date = req.query.date;
const id = req.query.id;
pool.query(`CALL Get_Alert_levels_Station(${id})`, function (error, result) {
if (error)
return res.status(500).json({ error: "Грешна заявка. Опитай отново !"})
aladinModel = result;
res.json({ ardaforecast })
});
});
// Start server
app.listen(HTTP_PORT, () => {
console.log("Server running on port %PORT%".replace("%PORT%", HTTP_PORT))
});
pool.on('error', function (err) {
console.log(err.code); // 'ER_BAD_DB_ERROR'
});
app.use(function (req, res) {
res.status(404);
})
;
Can I get an example of how I can fix this or how to find out where the problem is ?
You can use this one to see how what your url contains: https://www.freeformatter.com/url-parser-query-string-splitter.html
In your example, the problem is that you're using & (ampersand), but what it does is separating multiple query parameters. Since you have just one, your url is not properly structured and does not contain any parameters whatsoever.
You should use ? (question mark) to denote the first one:
localhost:3000/?id=13
p.s. Успех ;)

Make post request to Mongodb Atlas using Nodejs

I was familiar with MongodB for CRUD operation. Here, I'm trying to make simple post request on mongodB atlas but I want to know where I have done error for the connection and posting data to MongodB atlas.
Model.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let quizSchema = new Schema({
title: {
type: String,
},
description: {
type: Number,
},
question: {
type: String,
},
});
const Quiz = mongoose.model("Quiz", quizSchema);
module.exports = Quiz;
index.js
I'm trying to create the database collection name "QuizDatabase" and insert the data to it.
var express = require("express");
var bodyParser = require("body-parser");
const Quiz = require("./views/model/model");
var Request = require("request");
var app = express();
app.use(bodyParser.json());
const MongoClient = require("mongodb").MongoClient;
const uri =
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
mongoose.connect(uri);
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
app.post("/new/", function (req, res) {
Quiz.collection("QuizDatabase").insertMany(req.body, function (err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new quiz.");
} else {
res.status(201).send(JSON.stringify(body));
}
});
});
function handleError(res, reason, message, code) {
console.log("ERROR: " + reason);
res.status(code || 500).json({ error: message });
}
You dont have to use mongo client if you are already using mongoose.
In index.js file just import the model
const Quiz = require("./model");
And you are already using mongoose to connect to db when you write mongoose.connect(uri); You don't have to use client.connect() again.
Query to insert -
Quiz.insertMany(req.body);
Your index file should look like this -
const Quiz = require("./views/model/model");
var Request = require("request");
var app = express();
app.use(bodyParser.json());
const uri =
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
mongoose.connect(uri);
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
app.post("/new/", function (req, res) {
Quiz.insertMany(req.body, function (err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new quiz.");
} else {
res.status(201).send(JSON.stringify(body));
}
});
});
function handleError(res, reason, message, code) {
console.log("ERROR: " + reason);
res.status(code || 500).json({ error: message });
}
There are several reasons.
Connection Issues to the MongoDB database.
To check this insert app.listen() into mongoose connect. This would make sure you can only run development on your preferred PORT only when it has successfully connected to your Database. e.g From your code
mongoose.connect(uri)
.then(() => {
//listen for PORT request
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
}).catch((error) => {
console.log(error);
});
Try purposely using the wrong Username or Password and see if you get this error:
MongoServerError: bad auth : Authentication failed.
at Connection.onMessage (/Users/user/Documents/..<pathway>../connection.js:207:30)
*
*
*
*
ok: 0,
code: 8000,
codeName: 'AtlasError',
[Symbol(errorLabels)]: Set(1) { 'HandshakeError' } }
If you don't get this error then you have a connection problem. To solve this, I added my current IP ADDRESS and 0.0.0.0/0 (includes your current IP address) at the Network Access page. So you click on MY CURRENT IP ADDRESS and confirm upon setting up the network. Go to NETWORK ACCESS, click on add new IP ADDRESS, input 0.0.0.0/0 and confirm. Then try using the wrong username or password in the URI link given to you to see if it gives the above-expected error, then you can now correct the Username and Password, and npm run dev or npm start (However you configured it in your package.json file).
Code issues
First of I would correct your Model.js file from this:
const Quiz = mongoose.model("Quiz", quizSchema);
module.exports = Quiz;
to this:
module.exports = mongoose.model("Quiz", quizSchema);
I can see why yours can work, but it may be an issue as you want to get the schema upon accessing the whole file.
Secondly, I would correct the code for Posting and you can do that in 2 ways using the asynchronous method. Which depends on the method of assigning the req.body.
Way 1:
app.post("/new/", async (req, res) => {
const { title, description, question } = req.body;
//adds doc to db
try {
const quiz = await Quiz.create({ title, description, question });
res.status(200).json(quiz);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
OR
Way2:
app.post("/new/", async (req, res) => {
const quiz = new Quiz(req.body);
//adds doc to db
try {
const savePost = await quiz.save();
response.status(200).send(savePost);
} catch (error) {
response.status(400).send(error);
}
});
NOTE: You don't necessarily have to create a named database and collection in Mongo Atlas before starting the project. The URI given to you covers that if there are no problems with the connection to the DB or the Code.
based on your code
URI:
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
would create a database called: QuizDatabase and collection called: quizs (MongoDb always creates the plural word from the model given and makes it start with lowercase (i.e from your Model.js, the mongoose.model("Quiz"))).
If no database is named in your URI, then a database called test is automatically created for you as a default database, with the collection name being the mongoose.model("") given.
CONCLUSION
This should solve at least 90% of your issues, any other creation/POST problems is currently beyond my current expertise. Happy Coding 🚀🚀🚀

NodeJs Database Lookup

I'm using a networking website in node using express and postgresql - using the pg npm module - as my database.
I'm now trying to setup user pages like this:
...
app.get('/u/:u/', function (req, res) {
pool.connect(function(err, client, done) {
...
client.query('SELECT * FROM users WHERE id =($1)', [req.params.u], function(err, result) {
... done(); ...
});
});
});
...
req.params.u returns undefined inside the DB query.
I assume it's because the call back function isn't in the same scope as the app route, how do I get about checking the database like this?
I have a feeling there's a much better way of doing this.
So if the problem is that params gets undefined, the solution should come from something similar to:
...
app.get('/u/:u/', function (req, res) {
var tempReq = req
pool.connect(function(err, client, done) {
...
client.query('SELECT * FROM users WHERE id =($1)',[tempReq.params.u], function(err, result) {
... done(); ...
});
});
});
...
Make sure to put your body-parser code above all the routes in your app.js file to get req.body or req.params or req.query value
var app=express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
And your req object is available in your whole route's scope then you can use it in any where in this block no need to store in local variable
app.get('/u/:u/', function (req, res) {
pool.connect(function(err, client, done) {
...
client.query('SELECT * FROM users WHERE id =($1)',[req.params.u], function(err, result) {
... done(); ...
});
});
});
You can communicate with more than one database at a time too
You need ssl first..
var fs = require('fs');
var SSLkey = fs.readFileSync('/local/pem/location/key.pem');
var SSLcert = fs.readFileSync('/local/pem/location/cert.pem');
Now the database code
const mysql = require('mysql');
const forumdb_db = mysql.createConnection({
host: 'localhost',
user: 'dbuser',
password: 'dbpassword',
database: 'dbname',
key: SSLkey,
cert: SSLcert,
connectionLimit: 15,
queueLimit: 30,
acquireTimeout: 1000
});
const inventory_db = mysql.createConnection({
host: 'localhost',
user: 'dbuser',
password: 'dbpassword',
database: 'dbname',
key: SSLkey,
cert: SSLcert,
connectionLimit: 15,
queueLimit: 30,
acquireTimeout: 1000
});
Connecting to databases
inventory_db.connect((err0) => {
inventory_db.query("SELECT `bubblebum` FROM `user_inventory` WHERE `index`='1' LIMIT 1;", function(err0, result0, field0) {
console.log(result0[0].bubblebum);
});
});
forum_db.connect((err0) => {
forum_db.query("SELECT `stars` FROM `user_inventory` WHERE `stars`>'100' LIMIT 1;", function(err1, result1, field1) {
console.log(result1[0].stars);
});
});
There is one problem with this, You'll be disconnected after a while of inactivity so you should set up an interval to send a quick request every ten seconds to the sql server.

How to resolve multiple queries together using Sequelizejs

I have this basic nodejs script:
var express = require('express'),
Sequelize = require('sequelize'),
promise = require('bluebird'),
app = express(),
optimus = new Sequelize('optimus', 'root', 'test', {host: '127.0.0.1', dialect: 'mysql'}),
query = 'SELECT id FROM borrowers LIMIT 0,10',
query2 = 'SELECT COUNT(*) FROM borrowers';
app.get('/', function(req,res) {
var chain = new Sequelize.Utils.QueryChainer();
console.log('begin');
chain.add(optimus, 'query', [query,null,null,[]])
.add(optimus, 'query', [query2,null,null,[]])
.run()
.success(function() {
console.log('done');
}).error(function(err) {
console.log('oh no');
});
console.log('end');
res.send('Hi Ma!');
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
}
);
Neither 'done' nor 'oh no' ever fires which leads me to believe that I can' chain raw queries in this manner.
What I'd really like to accomplish is to asynchronously resolve both queries and pass the results back via res.send().
I have to admit to being a complete n00b at nodejs so any insights into how to correctly structure this would be greatly appreciated.
The major issue with your code is the fact, that you are sending a response to the client/browser too early. Instead of res.send-ing at the end of the app.get method, you need to send the answer inside the success respectively inside the error callback. Here you are:
var express = require('express'),
Sequelize = require('sequelize'),
promise = require('bluebird'),
app = express(),
optimus = new Sequelize('sequelize_test', 'root', null, {host: '127.0.0.1', dialect: 'mysql'}),
query = 'SELECT id FROM borrowers LIMIT 0,10',
query2 = 'SELECT COUNT(*) as count FROM borrowers';
app.get('/', function(req,res) {
var chain = new Sequelize.Utils.QueryChainer();
console.log('begin');
chain
.add(optimus.query(query, null, { raw: true }))
.add(optimus.query(query2, null, { raw: true, plain: true }))
.run()
.success(function(results) {
res.send({
resultOfQuery1: results[0],
resultOfQuery2: results[1]
});
}).error(function(err) {
console.log('oh no', err);
});
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
}
);
Please notice, that I changed the credentials to my local ones. Furthermore also check the arguments of chain.add. Instead of passing the values for an upcoming serial executation, we just throw the actual asynchronous methods into it and let the querychainer handle their promises.

Resources