How to export an async function inside a router in express? - node.js

I have the following route in index.js file-
router.post(/login, async (req,res) => {
async function getUser(id, data){
... Some logic
}
})
How can I export this function so that I can import it and call from another file i.e. register.js which has it's own route.?
Tried module.exports = getUser()
But it says it's not defined.

try defining the function outside the router handler function
async function getUser(id, data){
... Some logic
}
router.post(/login, async (req,res) => {
// getUser should be accessible inside here
})
module.exports = getUser
To accommodate your comment/edit where you mentioned that getUser will adapt the incoming res data to be exported, we need to define a state object outside the getUser function. At least from a functional perspective this will work, but I believe there are better ways to tidy this:
let state = {}
async function getUser(id, data){
// Some logic
// use res here by accessing the state object
}
router.post(/login, async (req,res) => {
// getUser should be accessible inside here
state = res;
})
module.exports = getUser

Related

TypeScript/Eslint throwing a 'Promise returned' error on a Express Router async route

I have the following endpoint setup to reset a database after test runs:
import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();
const resetDatabase = async (): Promise<void> => {
const connection = getConnection();
await connection.dropDatabase();
await connection.synchronize();
};
// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
await resetTestDatabase();
response.status(204).end();
});
export default router;
The entire route since async is underlined with a typescript-eslint error Promise returned in function argument where a void return was expected.
The app works perfectly but I'm not sure if I should be doing a safer implementation or just ignoring/disabling Eslint for this one. Any idea of what's wrong with that code?
I found a solution that doesn't involves using then() and let you use the abstraction of async without getting cursed by the eslint, there's two solutions (but i recommend more the second one)
First Solution: Using "inside async"
This is basic using a async inside the void like this:
router.post('/reset', (_request, response) => {
(async () => {
await resetTestDatabase();
response.status(204).end();
})()
});
Second Solution (recommended): "Type Overlap"
The second option is to you use it as an async, as Always, but say "hey TypeScript, nothing is wrong here hehe" with the "as" keyword
import { RequestHandler } from 'express'
router.post('/reset', (async (_request, response) => {
await resetTestDatabase();
response.status(204).end();
}) as RequestHandler);
It seems you are using the no-misused-promises rule which states that you cannot return Promise<void> in a place where void is expected.
This means that you cannot return Promise<void> from your Express handler because the return type of RequestHandler from the library specifies that the return type should be void. I would suggest that you change it to return Promise<Response> by adding a simple return keyword:
import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();
const resetDatabase = async (): Promise<void> => {
const connection = getConnection();
await connection.dropDatabase();
await connection.synchronize();
};
// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
await resetTestDatabase();
return response.status(204).send(); // <----- return added here
});
export default router;
The other option would be to avoid using async/await:
router.post('/reset', (_request, response) => {
resetDatabase().then(() => response.status(204).send());
});
After also going around the houses a little, here is an update:
Express 5 now handles this correctly (and is now in beta). Confirmation here from the Express maintainers:
Typings however are not yet (at the time of writing) updated/available for Express 5 so the error still displays, despite being resolved. Follow here on the DefinitelyTyped repo for conversation on updates.
In the meantime, as suggested already by KuryKat, handling with the RequestHandler should be safe in Express 5.
import { RequestHandler } from 'express'
router.post('/reset', (async (_request, response) => {
await resetTestDatabase();
response.status(204).end();
}) as RequestHandler);
In Express 4 however, better to handle the error either with an explicit .catch(...):
app.get("/manifest.webmanifest", (_req, res) => {
getManifest(pwaOptions)
.then(result => res.send(result))
.catch(error => res.status(500).send('Unexpected error'))
});
Or use an async handler:
express-async-handler
express-async-router
See discussion here regarding Express 4 for reference.

How to pass request object through promise chain

I am trying to get the request object as part of the then statement using the Node.js mssql package.
However, when I try to log it out, it is undefined.
exports.someFunction = (proc, req, res) => {
sql.connect(config.properties).then((pool, req) => {
return pool.request()
.execute(proc)
.then((response, req) => {
console.log(req) // undefined
})
}
How can I pass the request object to the then statement for comparison?
You've defined three separate function arguments as req so they all hide each other and you've tried to declare a 2nd parameter for a .then() handler which doesn't exist so of course it will be undefined.
You can direct access variables in the parent scope so that's all you need to do here:
exports.someFunction = (proc, req, res) => {
return sql.connect(config.properties).then((pool) => {
return pool.request()
.execute(proc)
.then((response) => {
console.log(req) // can directly access the req in the parent scope
// do something with `req` here
})
}
If you want to keep scope, perhaps a better way would be to write this as async/await:
exports.someFunction = async (proc, req, res) => {
const pool = await sql.connect(config.properties)
const result = await pool.request().execute(proc)
console.log(result, req) // both retain scope
})
}
But I think the reason why req is undefined in your console.log is because in:
sql.connect(config.properties).then((pool, req) => {
You are expecting req to be passed to you as a result of the .then() (which is a shadowed variable). If you remove it from here and the other .then() then it should also work

Pass object initialized via express middleware to next middleware

Below is my setup, I am trying to pass an object that is initialized in express middleware to a different middleware function. In my router, Invoke helper.getValues() and get an error that I cannot invoke function getValues of undefined
let helper; // no initial value
const getConfig = async () => {
config = await service.getConfig(configName);
helper = new Helper(config); // Helper is correctly initialized here
};
// declare a new express app
let app = express();
app.use(async function (req, res, next) {
try {
await getConfig(); // invoke the code that initializes my helper
next();
} catch (e) {
console.error(e);
}
});
app.use('/path', MyRouter(helper)); // Pass helper to router - it's undefined in router code
My Router Constructor looks like this
function MyRouter(helper) {
...
... const values = helper.getValues();
}
What is the correct way to pass the helper that is created in getConfig to my router?
Pass it via req or res depending on your intent.
If the data relates to the request such as the requesting user's identity, the session attributes, geoIP or parsed request body then attach it to the req object:
If the data relates to response processing such as variables used by templates/views or requested response format then attach it to the res object.
Assuming you want to pass it via req:
req.helper = await getConfig();
Then to use it:
function router (req, res) {
const values = req.helper.getValues();
// ...
}

Mock method and call through original callback

I have a method that I am trying to test that is invoked through a callback like so:
sessionService.getSession(req, res, req.query.state, handleSessionResponse);
Is there a way, using Sinon, to mock the getSession method and call through to the original callback (handleSessionResponse) in my function? In other words, I do not care about the implementation of getSession but I would like to delegate to the handleSessionResponse anyway to test it.
Here is a sample of the file:
handleSessionResponse(err, data, response) {
// get to this point and make assertions
}
sessionService.getSession(req, res, req.query.state, handleSessionResponse);
Give this a try.
const sinon = require('sinon')
const { mockReq, mockRes } = require('sinon-express-mock')
let request = {
query:{
state:{}
}
}
const req = mockReq(request)
const res = mockRes()
sinon.stub(sessionService, 'getSession').callsFake((req, res, req.query.state, callback) => {
callback(null, {}, {})
});
callback should call your original handleSessionResponse

This is undefined in NodeJS

SO here is my route.js file, which handles all the routes.
// Import post controller
const PostController = require('../controllers/post');
// Call post controller for API
router.post('/posts', PostController.create);
And then there is post.js file in controllers, which exports Post class.
const PostModel = require('../models/post');
class Post
{
async create ()
{
response = {};
let posts = await PostModel.find({});
response.additionalInfo = this.getAdditionalInfo();
res.status(200).send({response});
}
getAdditionalInfo ()
{
// returns some data for users and something
}
}
module.exports = new Post();
Now My question is how do i call getAdditionalInfo() from create method? because if i try this.getAdditionalInfo() i get undefined error.
This is how create is being used:
router.post('/posts', PostController.create);
With your
router.post('/posts', PostController.create);
, router.post is accepting a function named create as a callback. This means that when it's invoked, for example, if the internal code for router.post looks something like this:
(url, callback) => {
on(someevent, () => {
callback();
});
}
The calling context is missing. It's not calling PostController.create(), it's just calling someCallbackVariableName(). So, without a calling context, the this inside of create is undefined as a result.
Instead, either pass a function that invokes create with the proper calling context:
router.post('/posts', () => PostController.create());
Or use .bind to explicitly set the calling context to PostController:
router.post('/posts', PostController.create.bind(PostController));

Resources