I am using Expressjs and the Auth0 API for authentication and ReactJs for client side.
Because of the limitations of the Auth0 API (spoke with their team) I am sending updated user details to my backend and then using app.set() to be able to use the req.body in another route.
I need to call the app.patch() route automatically after the app.post() route has been hit.
The end goal is that the users data will be updated and shown client side.
const express = require('express');
const cors = require('cors');
const path = require('path');
const app = express();
require('dotenv').config()
const { auth } = require("express-openid-connect");
app.use(express.json());
app.use(cors());
app.use(express.static(path.join(__dirname, 'build')));
app.use(
auth({
issuerBaseURL: process.env.AUTH0_ISSUER_BASE_URL,
baseURL: process.env.BASE_URL,
clientID: process.env.AUTH0_CLIENT_ID,
secret: process.env.SESSION_SECRET,
authRequired: false,
auth0Logout: true,
})
);
app.get('/', async (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/api', async (req, res) => {
const stripe = require('stripe')(`${process.env.REACT_APP_Stripe_Live}`);
const invoice = await stripe.invoices.list({
limit: 3,
});
res.json(invoice);
});
app.post('/updateuser', (req, ) => {
app.set('data', req.body);
})
app.patch(`https://${process.env.AUTH0_ISSUER_BASE_URL}/api/v2/users/:id`,(req,res) => {
let val = app.get('data');
req.params = {id: val.id};
console.log(req.params);
})
app.listen(process.env.PORT || 8080, () => {
console.log(`Server listening on 8080`);
});
I'd suggest you just take the code from inside of app.patch() and make it into a reusable function. Then it can be called from either the app.patch() route directly or from your other route that wants to do the same funtionality. Just decide what interface for that function will work for both, make it a separate function and then you can call it from both places.
For some reason (which I don't really understand, but seems to happen to lots of people), people forget that the code inside of routes can also be put into functions and shared just like any other Javascript code. I guess people seems to think of a route as a fixed unit by itself and forget that it can still be broken down into components and those components shared with other code.
Warning. On another point. This comment of yours sounds very wrong:
and then using app.set() to be able to use the req.body in another route
req.body belongs to one particular user. app.set() is global to your server (all user's requests access it). So, you're trying to store temporary state for one single user in essentially a global. That means that multiple user's request that happen to be in the process of doing something similar will trounce/overwrite each other's data. Or worse, one user's data will accidentally become some other user's data. You cannot program a multi-user server this way at all.
The usual way around this is to either 1) redesign the process so you don't have to save state on the server (stateless operations are generally better, if possible) or 2) Use a user-specific session (like with express-session) and save the temporary state in the user's session. Then, it is saved separately for each user and one user's state won't overwrite anothers.
If this usage of app.set() was to solve the original problem of executing a .patch() route, then the problem is solved by just calling a shared function and passing the req.body data directly to that shared function. Then, you don't have to stuff it away somewhere so a later route can use it. You just execute the functionality you want and pass it the desired data.
Related
I am writing my first very simple express server for data a collection purpose. This seems like a beginner question but I failed to find an answer so far. The data is very small (less than 500 integers) and will never grow, but it should be able to be changed through POST requests.
I essentially (slightly simplified) want to:
Have the data in a .json file that is loaded when the server starts.
On a POST request, modify the data and update the .json file.
On a GET request, simply send the .json containing the data.
I don't want to use a database for this as the data is just a single small array that will never grow in size. My unclarities are mainly how to handle modifying the global data and file reading / writing safely, i.e. concurrency and how exactly does Node run the code.
I have the following
const express = require('express');
const fs = require('fs');
let data = JSON.parse(fs.readFileSync('./data.json'));
const app = express();
app.listen(3000);
app.use(express.json());
app.get("/", (req, res) => {
res.sendFile('./data.json', { root: __dirname });
});
app.post("/", (req, res) => {
const client_data = req.body;
// modify global data
fs.writeFileSync("./data.json", JSON.stringify(data), "utf8");
});
Now I have no idea if or why this is safe to do. For example, modifying the global data variable and writing to file. I first assumed that requests cannot run concurrently without explicitly using async functions, but that seems to not be the case: I inserted this:
const t = new Date(new Date().getTime() + 5000);
while(t > new Date()){}
into the app.post(.. call to try and understand how this works. I then made simultaneous POST requests and they finished at the same time, which I did not expect.
Clearly, the callback I pass to app.post(.. is not executed all at once before other POST requests are handled. But then I have a callback running concurrently for all POST requests, and modifying the global data and writing to file is unsafe / a race condition. Yet all code I could find online did it in this manner.
Am I correct here? If so, how do I safely modify the data and write it to file? If not, I don't understand how this code is safe at all?
Code like that actually opens up your system to race conditions. Node actually runs that code in a single-threaded kind of way, but when you start opening files and all that stuff, it gets processed by multiple threads (opening files are not Node processes, they are delegated to the OS).
If you really, really want to use files as your global data, then I guess you can use an operating system concept called Mutual Exclusions. Basically, its a 'lock' used to prevent race conditions by forcing processes to wait while something is currently accessing the shared resource (or if the shared resource is busy). In Node, this can be implemented in many ways, but one recommendation is to use async-mutex library to handle concurrent connections and concurrent data modifications. You can do something like:
const express = require('express');
const fs = require('fs');
const Mutex = require('async-mutex').Mutex;
// Initializes shared mutual exclusion instance.
const mutex = new Mutex()
let data = JSON.parse(fs.readFileSync('./data.json'));
const app = express();
app.listen(3000);
app.use(express.json());
app.get("/", (req, res) => {
res.sendFile('./data.json', { root: __dirname });
});
// Turn this into asynchronous function.
app.post("/", async (req, res) => {
const client_data = req.body;
const release = await mutex.acquire();
try {
fs.writeFileSync('./data.json', JSON.stringify(data), 'utf8');
res.status(200).json({ status: 'success' });
} catch (err) {
res.status(500).json({ err });
finally {
release();
}
});
You can also use Promise.resolve() in order to achieve similar results with the async-mutex library.
Note that I recommend you to use a database instead, as it is much better and abstracts a lot of things for you.
References:
Node.js Race Conditions
I need strategy on organizing the urls.
Our application has around 150 urls. Some are rather simple without any Route parameters. While others have often more than 1 route parameter in them. For example it could be like this
api/School/Class
api/School/1/Class/2/Student
api/School/Class/revaluate
So in the first one it has no parameter , while second has two parameters and finally third one has 1 but last part is not a resource but an action.
I don't want to store the url just where we would consume it, since maintaining the urls would be technical nightmare. I was hoping if we could have single file api.js or multiple files like api/School.js , api/Teacher.js for storing the files.
In express, you call this a router.
const express = require('express');
const app = express();
const schoolRouter = require('./routers/school');
//...
app.use('/api/school', schoolRouter); // Forwards any requests to the schoolrouter
//...
School.js:
// The router works just like express app (which is also a router)
const schools = require('express').Router();
// we used example.com/api/schools/something to get here
// but the router only cares about what comes next
schools.get('/', function(req, res, next) {
// res.send()
});
// Get a single school (etc, you get the point)
schools.get('/:schoolId', function(req, res, next) {
let id = req.params.schoolId;
// get data from server and res.send()
});
//...
module.exports = schools;
And you can chain routers, but because you only have a partial route, parameters might get lost. So the normal thing is to store parameters to the req object.
schools.use('/:schoolId/classes', function(req, res, next) {
req.schoolId = req.params.schoolId;
next()
}, classesRouter);
This way we can access req.schoolId at any time further down the chain.
my scenario id a nodejs + express app connected to a mysql database.
There are some context configurations, valid for all the website and all users, that I need to load from the database at every page request (not at app init because I don't want to reload the entire server when the configuration changes).
My idea was to use a simple middleware to inject the context into the request, like that:
const express = require('express');
const contextSetup = require('./contextSetup');
const app = express();
...
app.use(contextSetup.middleWare);
...
and in contextSetup.js:
const DB = require('./DB');
module.exports.middleWare = (req, res, next) => {
DB.query('select `code`, `data` from `setting` order by `sort`')
.then((rows) => {
const keyVals = {};
rows.forEach((row) => {
keyVals[row.code] = row.data;
});
req.appContext = keyVals;
next();
})
.catch((err) => next(err));
};
The only warning is to use the middleware after app.use(express.static()); otherwise my middleware would be called even for static files request (images, css, js, ...).
Is this approach correct?
Thank you
this works in my book! I might suggest that you put this on a router, so this works only for logged in users, for example. You might also further isolate your routers so that if there are any ajax routes, that these values are not requeried only to be ignored. So the code seems fine, and if performance becomes an issue, just implement a cache which, say, refreshes every half minute or ten requests. But its only one query, so that seems fine for now.
As an exercise in learning NodeJS, I am building a sort of API with ExpressJS that responds to web requests. As of right now, there are three routes in the program, '/login', '/register', and '/changePassword'. All of these methods do not need any sort of token to be processed.
However, every other route I plan to add to the program, (for example, a '/post' route) would require that the user authenticate themselves with a token obtained from a POST request to '/login' with the correct credentials.
TO verify the Token, I have written a middleware function:
module.exports.validateToken = function (req,res,next) {
const token = req.headers['x-access-token']
console.log(`validateToken() - TOKEN: ${token}`)
if (token) {
//Make sure the token is valid[...]
next()
}else {
return res.status(401).send({
message: 'Missing token',
success: false
})
}
}
My question is, how do I apply this middleware to only the routes that would require authentication?
I've thought of just creating another Router object, and calling it like this:
const tokenValidator = require('./util').validate.validateToken
// Router used for any actions that require user-authentication
const authRouter = new app.Router()
authRouter.use(tokenValidator)
But would this interfere at all with my original, authentication free routes?
// Initiate the routes that don't need auth
const routes = require('./routes')(app)
Thanks in advance, I am more of a Java developer, so a lot of the Javascript quirks have left me stumped.
Let's say your middleware is in "./middleware/auth"
I would create a base route for which the middleware should be applied, e.g.
app.use("/private", require("./middleware/auth"));
This will invoke your auth middleware, on any route which starts with '/private'
Thus, any API controller which requires auth should then be defined as:
app.use("/private/foo", require("./controllers/foo"));
Your middlware function will be invoked for any route within /private, before it hits your controller.
And any that do not require your middleware, should simply stay outside of the 'private' api context, e.g.
app.use("/", require("./controllers/somecontroller"));
In Expressjs, every middleware you add, gets added to the middleware stack, i.e. FIFO.
Thus, if you have certain routes, which you'd like to have no authentication, you can simply keep their middlewares above others.
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use(<<pattern>>, authenticate)
Additionally, you can try using nodejs basic-auth module for authentication
Hope this helps!
As far as I can gather from the Express documentation, when you declare an express.Router(), it creates a single instance of a router that you can then assign a routing path and execute logic with. The documentation says to think of a router like a mini-app for a specific route or routes, which makes sense.
I'm trying to strategize what to wrap my database connection around (using mongodb, let's say via mongoose, but it shouldn't matter). I of course don't want to open a new database connection on every route call, and I assume that if I wrap it around a router only one Router() instance will only be created. In other words, if I went to /routes/index.js, defined a Router() and then opened a database connection, then within it did router.get (or router.post, etc.), I would be opening one database connection when launching the app, not one per hit on that route.
Yet there might be other routes beyond index.js where I want access to the database. So alternatively, could I wrap the database connection around the app.use route handlers and other middleware within the main app.js file, then require('../app') in /routes files and add the database connection to module.exports in app.js, and finally define route logic in other files like /routes/index.js?
I'm a little confused on how to structure everything. Any guidance would be much appreciated.
If you are using mongoose, you can just connect once with some code like this:
mongoose.connect("mongodb://127.0.0.1:27017/test");
mongoose.connection.on('error', console.error.bind(console, 'Db connection error:'));
// Start the server once we have connected to the database.
mongoose.connection.once('open', () => {
console.log('Db connection open.');
app.listen(3000, function () {
console.log('Listening on port 3000');
});
});
And then, if you have a mongoose model named Foo set up like
const Foo = mongoose.model('Foo', fooSchema); // fooSchema is a mongoose.Schema
Then in your route you can use it like so:
const router = express.Router();
const Foo = require('./models/foo');
router
.route('/foos/:foo_id')
.get((req, res)=> {
Foo.findById(req.params.foo_id, (err, foo) => {
if (err) return res.sendStatus(500);
if (!foo) return res.status(404).send('Foo not found.');
res.status(200).json(foo);
});
});
This kind of setup lets mongoose handle connection pooling.