How to share information between Express middleware and endpoints? - javascript

Lots of middleware comes with factories, that takes an options object. Among the options is usually a function that needs to provide some necessary information to the middleware. As an example, take a look at express-preconditions:
app.use(preconditions({
stateAsync: async (req) => { // Fetch the date the resource was last modified. }}
});
This is a neat pattern, but I find it gets complicated when the same information is needed in multiple places. For instance, let's say I've got a database table that contains both the information about the resource that the response is supposed to contain, and the last modified date. In other words, the same information is needed in both the middleware and the endpoint itself. I end up with code similar to this:
//The middleware
app.use(preconditions({
stateAsync: async (req) => {
const data = await fetchFromDb(req.param("id"));
return {
lastModified: data.lastModified
};
})
//The endpoint
app.use("path", (req, res, next) => {
const data = await fetchFromDb(req.param("id"));
res.send(data);
});
I'm hitting the database twice just because I need the same info in different places. I could off course just fetch it once, or store it somewhere on the request object. But that feels a bit like a hack. Another solution would be to have some kind of caching mechanism in fetchFromDb, but that feels a bit overcomplicated.
In my experience, this is a quite common problem when building stuff with Express. What is the recommended way to deal with situations like this?

You can pass data between middlewares with res.locals:
app.get('/yourEndPoint', (req, res, next) => {
const data = // fetch your datas;
res.locals.lastModified = data.lastModified;
next();
}, (req, res) => {
const lastModified = res.locals.lastModified;
// do whatever you need to do
});

Related

How to create an express update route with multiple parameters

I want to update an attribute within a JSON object using fetch PUT. I've created a put function taking in 2 URL parameters
app.put('/trustRoutes/:id/:item', (req, res){
I am able to update the data with a single parameter but since I only want to change one value inside that object, calling put will replace the whole object with my new body.
below is what I've tried.
app.put('/trustRoutes/:id/:item', (req, res) => {
readFile(data => {
const userId = req.params['id/item'];
// have also tried const userId = req.params.id.item
data[userId] = req.body;
//write data back to file
I looked around at other examples but couldn't find any that were updating data instead of GET. If there is one I missed please let me know.
PUT requests are great for completely overwriting a resource, and is idempotent. This answer does a good job explaining idempotency. For updating a resource partially, a PATCH request is a better choice.
app.patch('/trustRoutes/:id/:item', (req, res) => {
readFile(data => {
data[userId] = req.params[id];
data[item] = req.params[item];
// A get request for this resource would now show both of the updated values
// Write file

Nodejs controller is being messy

I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.

Custom middleware for express routes

I actually implemented a custom middleware in an expressJs application. I aim for modifying the request object before sending it back. It works and the result is exactly what i was expecting, but i am not sure if this is the correct way to do so. I am kind of afraid, that manipulating the reqeust object is not the correct way to do this.
Here is the code:
expressApp.get('/apps/:id', isAuthenticated, appLinks(dbController), async (req, res) => {
res.send(req.__appLink)
})
app-links.ts:
export const appLinks: MiddlewareWrapper = (dbController: DatabaseHandler) => async (req: Request, res: Response, next: NextFunction) => {
dbResult = modifyMyDatabaseResult(await dbController.getApps());
req.__appLink = dbResult
next()
}
As you can see, in my route i send req.__appLink which was set inside the middleware. It this the correct way to do such things?
This is good! Nothing is wrong with modifying the req object.
Yes this is fine. But for better presentation you can save the result of req._appLink in a variable and pass it to res object.

Express js repeat route journey with context

I've been sitting with this for few days already and I'm brain dead from this and can't figure the best way to approach this.
What I would like to achieve is to be able to repeat the route journey with new context data on the start. So for rxample:
app.get('/test', testGet);
app.post('/test', testPost);
having those two routes I would like:
GO through testGet handler (fetch necessary data and present ui for Form)
Gather Form data and submit it for the testPost handler
Process data from form on testPost and generate appropirate new payload
Redirect again to the testGet handler with new payload data from testPost if journey not successful
How I would pass that new data to the testGet on redirection? Can't figure the best way.
Redirect doesn't accept any payload. Redirecting with query params is kinda way to go but it appends the url string with query string to the user which I would like not to be present to the user. And that data would not be a simple string but an object containing few arrays and properties
Look at this
const handleRequest = (req, res, next) => {
const view = 'myView.jsx';
let myData = {};
...do the common job
if (req.method === 'GET') {
return res.render(view, data)
}
/* here you are in the post method */
data = /* modify your data with the req.body */
return res.render(view, data)
};
app.get('/test', handleRequest);
app.post('/test', handleRequest);

Express server route format

I have two different types of possible API class I can make.
The first one is:
http://api_url.com/api/v1/schools/countries/BR
and the second one is:
http://api_url.com/api/v1/schools/countries/BR?admin1=MA
My route in backend/routes/schools.js is:
router.get('/countries/:country', forward_get);
const forward_get = (req, res, next) => {
console.log(req);
const url = `${url}${req.originalUrl}`
getResponse(url, acToken, res);
}
How do I make it so that I am able to also make the second api call and get the appropriate parameters "admin1: MA". Ive gone through the whole req object and I don't seem to find them anywhere. So far I've been able to make the first api call without a problem.
This is the only route you need:
You access admin1 using req.query.admin1
and
You access country using req.params.country

Categories

Resources