Change nodejs route with params at runtime - javascript

I need to change a route params at runtime in nodejs.
In my code I set a route using:
const troutes = require('./src/routes/testroutes')(page,sessionId)
app.use('/teste' + sessionId, troutes)
But, in runtime, sometimes the "page" object param is recreated and it becomes invalid. But the "sessionId" param is the same, no changes.
Then I create a new "page" and need remake the route, because the old route has a invalid page.
if I use:
let page={newpage};
const troutes = require('./src/routes/testroutes')(page,sessionId);
My urls don´t work because the route use the old invalid "page" param object.
How could I delete or change the old route and use the new route with the new "page" object?
//server.js
const express = require('express')
const app = express()
const server = require('http').Server(app)
const port = 3001
const host = 'http://localhost'
const callbackUrl = `${host}:${port}`;
let page={page};
let sessionId='sessionId';
const troutes = require('./src/routes/testroutes')(page,sessionId)
app.use('/teste' + sessionId, troutes)
....
});
//routes.js
const express = require('express');
const router = express.Router();
module.exports = function (page,sessionId) {
router.get('/'+sessionId+'/getMyNumber', async (req, res) => {
const cc = await page.evaluate(() => {
return getMyNumber();
});
console.log(cc);
res.send({ "data": cc });
});
return router;
}

since you called require('./src/routes/testroutes')(page,sessionId) method only once in file, it loads only once. so passed parameters/initializations within function executed only once.
the router which is initialized and returned is used for further request processing.So new request will only trigger method of router.
if you want page to be dynamic, it would work if you move page inside router, instead of passing it from wrapper function as below.
const express = require('express');
const router = express.Router();
module.exports = function (sessionId) {
router.get('/'+sessionId+'/getMyNumber', async (req, res) => {
let page = {newPage}
const cc = await page.evaluate(() => {
return getMyNumber();
});
console.log(cc);
res.send({ "data": cc });
});
return router;
}

Related

Bind problem in SQL query in Node, Express, Mysql2 app

I have been following a tutorial on setting up REST APIs in Node, using Express for an app that accesses an existing MariaDB database. My version only needs to read data and I have the DB co-located with the Node application (same host).
My goal for this entry-level example is to just access the data, using static SQL, so I can see it rendered in the web page by the JSON pritifier.
[Next, I want to present the data in a table (EJS?). Later, when I can get that to work, I'll add form controls (React?) to let a user specify start and end date bounds for the SQL query. Finally I'll aim to render the data as a line graph (D3js).]
The tutorial runs the web server successfully (it returns 'OK' on the base URL), but when I go to URL/solarData it tries an async function to getMultiple rows from the DB, it responds:
Bind parameters must not contain undefined. To pass SQL NULL specify JS null TypeError: Bind parameters must not contain undefined. To pass SQL NULL specify JS null
at /SunnyData/solarViz/node_modules/mysql2/lib/connection.js:628:17
at Array.forEach (<anonymous>)
at Connection.execute (/SunnyData/solarViz/node_modules/mysql2/lib/connection.js:620:22)
at /SunnyData/solarViz/node_modules/mysql2/promise.js:120:11
at new Promise (<anonymous>)
at PromiseConnection.execute (/SunnyData/solarViz/node_modules/mysql2/promise.js:117:12)
at Object.query (/SunnyData/solarViz/services/db.js:6:40)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Object.getMultiple (/SunnyData/solarViz/services/solarData.js:7:16)
at async /SunnyData/solarViz/routes/solarData.js:8:14
app.js:61
./app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3800;
const solarDataRouter = require('./routes/solarData');
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
app.get('/', (req, res) => {
res.json({'message': 'ok'});
})
app.use('/solarData', solarDataRouter);
/* Error handler middleware */
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
console.error(err.message, err.stack);
res.status(statusCode).json({'message': err.message});
return;
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
./routes/solarData.js
const express = require('express');
const router = express.Router();
const solarData = require('../services/solarData');
/* GET solar data. */
router.get('/', async function(req, res, next) {
try {
res.json(await solarData.getMultiple(req.query.page));
} catch (err) {
console.error(`Error while getting solar data `, err.message);
next(err);
}
});
module.exports = router;
./config.js
const env = process.env;
const config = {
db: {
host: env.SUNNY_HOST,
user: env.SUNNY_USER,
password: env.SUNNY_PW,
database: env.SUNNY_DB,
},
listPerPage: env.LIST_PER_PAGE,
};
module.exports = config;
./services/solarData.js
const db = require('./db');
const helper = require('../helper');
const config = require('../config');
async function getMultiple(page = 1){
const offset = helper.getOffset(page, config.listPerPage);
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
const data = helper.emptyOrRows(rows);
const meta = {page};
return {
data,
meta
}
}
module.exports.getMultiple = getMultiple;
./services/db.js
const mysql = require('mysql2/promise');
const config = require('../config');
async function query(sql, params) {
const connection = await mysql.createConnection(config.db);
const [results, ] = await connection.execute(sql, params);
return results;
}
module.exports = {
query
}
I've left out the ./helper.js
Everything runs fine until I direct the webpage to /solarData. At that point I get the Debug Console (vscode) mentioned up-front
Searching seems to point at a mysql2 shortcoming/bug but not at a practical solution
If you respond, please describe the 'bind' mechanism, as I'm not sure what's going on.
Hope I've put enough info in. Please ask if I need to add anything else.
The error says
Bind parameters must not contain undefined.
It means that in the file ./services/solarData.js on the line
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
Some of the 2 variables is undefined, you need to check offset and config.listPerPage to be defined.
Just use
console.log('offset: ' + offset)
console.log('listPerPage: ' + config.listPerPage)
and you will find out what is undefined in your case

Axios post request failing with a 404

I'm using Axios to query an endpoint in my backend. When I try and do this, I get a 404 not found. If I copy/paste the uri it gives in the error from the console and try and access it directly in the browser it connects fine and does not give me an error (instead giving me an empty object which is expected).
Below is my Axios code
axios.post("/api/myEndpoint", { id: this.userID })
.then((response) => {
this.property = response.data.property;
})
.catch((errors) => {
console.log(errors);
router.push("/");
});
Below is the route definition in my backend
const myEndpointRoute = require('../api/myEndpoint.js')();
exprApp.use('/api/myEndpoint', myEndpointRoute);
For reference, the uri is 'http://localhost:3000/api/myEndpoint'. I can access this uri completely fine in the browser but Axios returns a 404 as described above. It is for this reason that I'm confident this is an issue in the frontend, however I have set up this Axios request in the same way as the many others I have and they all work fine.
Edit: here's the rest of the backend
myEndpoint.js
module.exports = function() {
const express = require('express'), router = express.Router();
const authMiddleware = require('../loaders/authMiddleware.js')();
router.get('/', authMiddleware, async function(req, res) {
const id = req.body.id;
const property = await require('../services/myEndpointService.js')
(id).catch((e) => { console.log(e) });
res.send({ property: property });
});
return router;
};
myEndpointService.js
module.exports = async function(id) {
const results = await require('../models/getMyEndpointProperty')(id);
return results;
};
getMyEndpointProperty
module.exports = async function(id) {
const pool = require('../loaders/pool.js')();
const res = await pool.query(`SELECT * FROM myTable WHERE id = ${id};`);
return res.rows;
};
myEndpoint.js defines only a GET method but your axios call sends a POST in the frontend. Try changing (or adding) the express route:
// notice the `.post`
router.post('/', authMiddleware, async function(req, res) {
...
})
It worked when you manually tested it in the browser for this reason as well, since the browser sent a GET request.

How to pass data from one route to another nodejs with expressjs

I want to add a certain attribute to the request object and access the added it from another route after redirecting.
The following code shows what I exactly need.
const express = require('express')
const app = express()
app.get('/test1',(req,res)=>{
console.log('test1')
req.name = 'lahiru'
res.redirect('/test2')
})
app.get('/test2',(req,res)=>{
console.log('test2')
let val = req.name
console.log(val)
})
app.listen(3000)
But when I sends a request to the '/test1', I get the following output.
test1
test2
undefined
I tried this with express-session, but log-returns the same 'undefined'.
Can anyone please help.
Thanks in advance.
You can do one of the following if you want name available in /test2:
Pass the data in the URL using querystring: /test2?name={name}
Change the route to: /test2/:name
Use cookies
Other type of session
app.get('/test1',(req,res)=>{
const name = 'lahiru'
res.redirect(`/test2/${name}`)
// res.redirect(`/test2/?name=${name}`)
})
// using req.params
app.get('/test2/:name',(req,res)=>{
let val = req.params.name
console.log(val)
})
// using query string
app.get('/test2',(req,res)=>{
let val = req.query.name
console.log(val)
})
you could use the first route as a middleware to modify the req param.
const express = require("express");
const app = express();
const middleware = (req, res, next) => {
console.log("test1");
req.name = "lahiru";
next();
};
app.get("/test2", middleware, (req, res) => {
console.log("test2");
let val = req.name;
console.log(val);
});
app.listen(3000);

Unit Testing Controllers use Jest, NodeJS

I want to check a case that certain routes are calling the correct controller use Jest specific (mock or spy).
It is case specific for unit testing. Somebody can help me how to check it use jest. I don't need verify kind of
expect (status code or res object) i need to check if controller have been called.
Thanks!
For instance:
// todoController.js
function todoController (req, res) {
res.send('Hello i am todo controller')
}
// index.spec.js
const express = require('express');
const request = require('request-promise');
const todoController = require('./todoController');
jest.mock('./todoController');
const app = express();
app.get('/todo', todoController)
test('If certain routes are calling the correct controller , controller should to have been called times one.', async() => {
await request({url: 'http://127.0.0.1/todo'})
expect(todoController).toHaveBeenCalledTimes(1);
})
Actually if you search, there are many references out there.
In the following, I share a few ways that I know.
One of the big conceptual leaps to testing Express applications with mocked request/response is understanding how to mock a chained
API eg. res.status(200).json({ foo: 'bar' }).
First you can make some kind of interceptor, this is achieved by returning the res instance from each of its methods:
// util/interceptor.js
module.exports = {
mockRequest: () => {
const req = {}
req.body = jest.fn().mockReturnValue(req)
req.params = jest.fn().mockReturnValue(req)
return req
},
mockResponse: () => {
const res = {}
res.send = jest.fn().mockReturnValue(res)
res.status = jest.fn().mockReturnValue(res)
res.json = jest.fn().mockReturnValue(res)
return res
},
// mockNext: () => jest.fn()
}
The Express user-land API is based around middleware. AN middleware that takes a request (usually called req), a response (usually called res ) and a next (call next middleware) as parameters.
And then you have controller like this :
// todoController.js
function todoController (req, res) {
if (!req.params.id) {
return res.status(404).json({ message: 'Not Found' });
}
res.send('Hello i am todo controller')
}
They are consumed by being “mounted” on an Express application (app) instance (in app.js):
// app.js
const express = require('express');
const app = express();
const todoController = require('./todoController');
app.get('/todo', todoController);
Using the mockRequest and mockResponse we’ve defined before, then we’ll asume that res.send() is called with the right payload ({ data }).
So on your test file :
// todo.spec.js
const { mockRequest, mockResponse } = require('util/interceptor')
const controller = require('todoController.js')
describe("Check method \'todoController\' ", () => {
test('should 200 and return correct value', async () => {
let req = mockRequest();
req.params.id = 1;
const res = mockResponse();
await controller.todoController(req, res);
expect(res.send).toHaveBeenCalledTimes(1)
expect(res.send.mock.calls.length).toBe(1);
expect(res.send).toHaveBeenCalledWith('Hello i am todo controller');
});
test('should 404 and return correct value', async () => {
let req = mockRequest();
req.params.id = null;
const res = mockResponse();
await controller.todoController(req, res);
expect(res.status).toHaveBeenCalledWith(404);
expect(res.json).toHaveBeenCalledWith({ message: 'Not Found' });
});
});
This is only 1 approach to testing Express handlers and middleware. The alternative is to fire up the Express server.

exported function returns 'undefined' when called inside app.get()

I defined a sequelize model in a file named courses.js as such:
const Sequelize = require('sequelize');
var connection = new Sequelize( ... );
module.exports = Courses = connection.define('courses', { ... });
Then required it in a file called coursescontroller.js:
const Courses = require('../models/courses');
function find_records() {
Courses.findAll().then(function(records) {
return records;
});
}
module.exports.find_records = find_records;
Then finally required the function in the express router file and used it as such:
var express = require('express');
var router = express.Router();
var controller = require('../controllers/coursescontroller');
router.get('/', function(req, res, next) {
var records = controller.find_records();
console.log(records); // console shows undefined
res.send(records); // doesn't send anything back when I make a get request
});
Could you please help me get the find_records() function to work properly and return the database records when called inside the get request callback function.

Categories