Let's say I have a few routes defined in express.
app.get('/all', async (req, res) => {
// foo, bar, baz are database calls
// since they depend on one another, they are run sequentially with await
const fooRes = await foo();
const barRes = await bar(fooRes);
const bazRes = await baz(barRes);
res.send({data: [fooRes,barRes,bazRes]})
});
app.post('/foo', async (req, res) => {
// updates foo
});
app.post('/bar', async (req, res) => {
// updates bar
});
app.post('/baz', async (req, res) => {
// updates baz
});
If a user requests /all, can another user update /foo while the /all handler is still await-ing from the result from baz(bar)? This would result in potentially stale/incorrect data coming back from the /all handler.
Conversely, if the database calls were not dependent on each other, and the 3 db calls were fired at the almost the same instant in the /all handler
app.get('/all', async (req, res) => {
const data = await Promise.all([
foo(),
bar(),
baz()
]);
res.send({ data })
});
// ... /foo, /bar, /baz update routes as above
Let's assume user1's request for /all hits express first, before user2's update request on /baz does.
Is it correct to assume that user1's baz() function call definitely executes before user2's /baz update?
Is it possible that a freak latency spike when querying the db can cause user1's /all request to return the updated baz value? ie. user2's /baz update actually reaches the db before user1's /all read queries.
Related
I'm creating a helper method in my code which I can use for authorizing users,
I am creating a function called "usePermissions" following the react way but on the back-end.
The usePermissions() will return true or false based on some x, y and z.
But this method depends on the request object,
so in order to make it work, I'll everywhere have to call
usePermissions(req),
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions(req)
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Is there a way to package this "usePermissions" helper method with the request object?
I want it to automatically have the request object, I don't want to keep passing it as a variable,
how to make it have the request object without having to pass it as an argument to it, is it possible?
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions() // <-- see, I want to not pass the req object
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
You can create a middleware that will call usePermissions and append result to the Request object, so it will become available in all your handlers without explicitly calling it.
Your middleware code might look something like (read more about using middlewares in Express app)
export function getUsePermissions = (req, res, next) => {
const { verify } = usePermissions(req);
req['verify'] = verify
// moving to the next middleware in chain, now Request object has a verify property
return next()
}
and in your express app, add getUsePermissions middleware
express.use(getUsePermissions);
now you can use extract usePermissions from request object in your handlers:
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = req['verify'];
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Yes, you can use it as middleware and you will get a response, request and next object by default.
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', usePermissions , async (req, res, next)=>{
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
usePermissions Function will be like this:
const usePermissions = (request, response, next) => {
const isPermitted = 'you logic';
if (isPermitted) {
next(); // this will automatically call the next middleware
// in your case it will call the next callback in the userRouter
} else {
response.send('You don't have permission'); // just added a sample text for brevity
}
}
You can read more about middlewares here.
I would like to have custome response time-out, so that I am trying to send the response from setTimeout callback if the processing time exceeds certain interval.
In the following example, I set 20ms as time-out period within which services.getReport() has to be processed otherwise the API has to send response as response_timed_out.
The following code works fine but it throws exception like
Cannot set headers after they are sent to the client
router.post("/getReport", (req, res) => {
setTimeout(() => {
res.send({ status:503, msg: "response_timed_out" });
}, 20);
services.getReport(req, res);
});
You can use setTimeout method on the request object to define timeout time for each route.
router.post("/getReport", (req, res) => {
req.setTimeout(2000);
services.getReport(req, res);
});
You can also define timeout time globally for all routes on the server.
const server = app.listen(port, () => {});
server.setTimeout(15000);
You can also provide the second argument which is callback that will run when timeout occurs. If you want the callback then you call setTimeout on the response.
router.post("/getReport", (req, res) => {
res.setTimeout(2000, () => res.send('timed_out'));
services.getReport(req, res);
});
I'm very new to express / NodeJS in general. I started to write a little application, providing a REST API. The whole thing should work like this:
the request gets routed (routes.js)
in routes.js, a function in a controller gets called
since my controller contains the app's logic, it should return data that gets sent with res.json(CONTROLLER_FUNCTION()).
My code:
controller.js
User = require('../models/user')
module.exports.users = function users() {
users = User.findAll().then(users => {
return users;
});
}
routes.js
/* GET users listing. */
router.get('/', function (req, res, next) {
res.json(userController.users())
});
So when my route gets called, nothing happens. I believe that happens because my controller logic is async. Probably I have to implement something like a wrapper/callback function (?)
So basically my question is: what is the "right" way to handle a situation like this? How should my code look like?
Example:
What I normally would do is to pass to userController.users a function, that gets called when the async action is done (.then).
routes.js
/* GET users listing. */
router.get('/', function (req, res, next) {
userController.users((data) => res.json(data));
});
controller.js
User = require('../models/user')
module.exports.users = function users(send) {
users = User.findAll().then(users => {
send(users);
});
}
Is this the best way to do this? / Is this considered good practice? Is it even recommended to not put my code directly into routes.js?
module.exports.getUsers = async () => {
const users = await User.findAll();
return users;
}
I have a general question on how you handle services and routes in node.js. Would you handle the response directly in the service or would you leave that to the route? Here's what i mean in code
Like this
Route
router.get('/', (req, res, next) ==> {
someService.someMethod(req, res);
});
Service
const someMethod = (req, res) => {
try {
var something = await someOtherMethod(req.body.someParameter);
return res.status(200).send(something.data);
} catch (err) {
return res.status(500).send({msg: err.message});
}
}
Or this
Router
router.get('/', (req, res, next) ==> {
try {
var something = await someService.someMethod(req.body.someParameter);
res.status(200).send(something.data);
} catch (err) {
res.status(500).send({msg: err.message})
}
});
Service
const SomeMethod = (Input) => {
return someOtherMethod(Input);
}
The first way would make the routers much simpler and cleaner especially if the use the service in multiple routes, but on the downside I always need to supply the res and req and I will run into problems if I want to use the service internally. I'm tending to the second method.
How do you design your services?
I would go for router.get('/', RootController)
const RootController = (req, res) => {
// extract what you need from the request
const param = req.body.param;
// calculate what you need in a pure function `businessLogic`
const result = businessLogic(param);
// send the response
return res.send(result);
}
This way you get a separation of concerns - your root controller is responsible only for handling / requests - getting a response for a request. All "business logic" is done in a pure function (you can easily test it without any HTTP request contexts/mocks, it can be reused somewhere else, for example in different controller).
I use the following architecture:
1. Route
2. Controller
3. Services
Your route is the one validating the input, your controller is the one handling all the logics and calling the services and returning the final result to your route.
I am writing some express middleware that needs to access a database. It is going to be released as a package so I want it to be as self contained as possible. I was wondering how I should handle the connection to the database. It is async(of course), but it only needs to happen once when the package is initialized. Where should this happen?
I was thinking something like this. The problems is, the middleware is passed back right away, before the database is ready.
// App
app.use(myMiddleware({
db: "<db connection string>"
});
// Middleware
module.exports = function(db) {
// Open db
return function(req, res, next) {
// Middleware stuff
}
}
I'd recommend against such a singleton, dependency injection is a better solution here, and a connection per app is hardly scalable. A connection pool might be a better idea.
That said, you can do something like:
var db = null; // to keep our instance
var asked = false; // to not make several requests that override each other
function openDb(db,ready){
// if we already have it, we return the instance
if(db !== null) ready(null,db);
// if it was already asked by another instance, keep track of it
// to avoid multiple requests.
if(asked) asked.push(ready);
asked = [];
openDbCode(db,function(err,result){
if(err) {
ready(err,null); // error case
asked.forEach(function(fn){ fn(err,null); }); // notify waiters of failure
}
db = result; // save the reference
asked.forEach(function(fn){ fn(db,null); }); // notify all waiters
})
}
This function effectively waits for a db for the first asker and then calls everyone on the same instance. Note that this function will use the first connection string provided.
module.exports = function(db) {
return function(req, res, next) {
openDb(db,function(err,db){
if(err) handleDbOpenErrorLogicHere();
// middleware stuff, same db available here, call next to continue
});
};
}
use an async function to wrap your create app code and then app.listen after everything is initialized.
// app.js
import express from "express";
export default async () => {
const app = express();
const asyncMiddleware) = await initMWAsync();
app.use(asyncMiddleware);
return app;
}
// entry point of your program
import createApp from "./app";
const server = createApp()
.then(app => app.listen(app.get("port"), () => {
console.log(
" App is running at http://localhost:%d in %s mode",
app.get("port"),
app.get("env")
);
console.log(" Press CTRL-C to stop\n");
}));
export default server;